static async Task DefaultRoute(HttpContext ctx) { DateTime startTime = DateTime.Now; string header = ctx.Request.Source.IpAddress + ":" + ctx.Request.Source.Port + " "; _Logging.Debug(header + ctx.Request.Method + " " + ctx.Request.Url.RawWithoutQuery); #region APIs try { #region Unauthenticated-Methods switch (ctx.Request.Method) { case HttpMethod.GET: #region get if (ctx.Request.Url.RawWithoutQuery.Equals("/")) { ctx.Response.StatusCode = 200; ctx.Response.ContentType = "text/html; charset=utf8"; await ctx.Response.Send(RootHtml()); return; } if (ctx.Request.Url.RawWithoutQuery.Equals("/favicon.ico") || ctx.Request.Url.RawWithoutQuery.Equals("/robots.txt")) { ctx.Response.StatusCode = 200; await ctx.Response.Send(); return; } break; #endregion case HttpMethod.PUT: #region put break; #endregion case HttpMethod.POST: #region post break; #endregion case HttpMethod.DELETE: #region delete break; #endregion default: ctx.Response.StatusCode = 400; ctx.Response.ContentType = "application/json"; await ctx.Response.Send(SerializationHelper.SerializeJson(new ErrorResponse(ErrorCodeEnum.InvalidRequest, "Unknown method", null), true)); return; } #endregion #region Build-Metadata RequestMetadata md = new RequestMetadata(ctx); #endregion #region Authenticate if (_Settings.Server.RequireAuthentication) { string apiKey = null; ApiKey key = null; if (!_Auth.Authenticate(ctx, out apiKey, out key)) { _Logging.Warn(header + "authentication failed"); ctx.Response.StatusCode = 401; ctx.Response.ContentType = "application/json"; await ctx.Response.Send(SerializationHelper.SerializeJson(new ErrorResponse(ErrorCodeEnum.NotAuthenticated, "You are not authorized to perform this operation", null), true)); return; } md.ApiKey = key; md.Params.ApiKey = apiKey; } if (md.Params.Metadata) { ctx.Response.StatusCode = 200; ctx.Response.ContentType = "application/json"; await ctx.Response.Send(SerializationHelper.SerializeJson(md, true)); return; } #endregion #region Authenticated-Methods switch (ctx.Request.Method) { case HttpMethod.GET: #region get if (ctx.Request.Url.RawWithoutQuery.Equals("/_databaseclients")) { await GetDatabaseClients(md); return; } if (ctx.Request.Url.RawWithoutQuery.Equals("/_databases")) { await GetDatabases(md); return; } if (ctx.Request.Url.Elements.Length == 1) { await GetDatabase(md); return; } if (ctx.Request.Url.Elements.Length == 2 || ctx.Request.Url.Elements.Length == 3) { await GetTableSelect(md); return; } break; #endregion case HttpMethod.PUT: #region put if (ctx.Request.Url.Elements.Length == 2 || ctx.Request.Url.Elements.Length == 3) { await PutTable(md); return; } break; #endregion case HttpMethod.POST: #region post if (ctx.Request.Url.Elements.Length == 1) { if (ctx.Request.Query.Elements.ContainsKey("raw")) { await PostRawQuery(md); return; } else { await PostTableCreate(md); return; } } if (ctx.Request.Url.Elements.Length == 2) { await PostTableInsert(md); return; } break; #endregion case HttpMethod.DELETE: #region delete if (ctx.Request.Url.Elements.Length == 2 || ctx.Request.Url.Elements.Length == 3) { await DeleteTable(md); return; } break; #endregion default: ctx.Response.StatusCode = 400; ctx.Response.ContentType = "application/json"; await ctx.Response.Send(SerializationHelper.SerializeJson(new ErrorResponse(ErrorCodeEnum.InvalidRequest, "Unknown method", null), true)); return; } #endregion ctx.Response.StatusCode = 400; ctx.Response.ContentType = "application/json"; await ctx.Response.Send(SerializationHelper.SerializeJson(new ErrorResponse(ErrorCodeEnum.InvalidRequest, "Unknown endpoint", null), true)); } catch (Exception e) { _Logging.Exception(e); ctx.Response.StatusCode = 500; ctx.Response.ContentType = "application/json"; await ctx.Response.Send(SerializationHelper.SerializeJson(new ErrorResponse(ErrorCodeEnum.InternalError, "Internal server error", e.Message, e), true)); } finally { _Logging.Debug(header + ctx.Request.Method + " " + ctx.Request.Url.RawWithoutQuery + " " + Common.TotalMsFrom(startTime) + "ms: " + ctx.Response.StatusCode); } #endregion }
private void RunSetup() { #region General if (Console.WindowWidth < 79) { Console.WindowWidth = 79; } Settings ret = new Settings(); ret.Version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; ret.Logging = LoggingSettings.Default(); ret.ApiKeys = new List <ApiKey>(); ret.ApiKeys.Add(ApiKey.Default()); #endregion #region Server-Settings // 0 1 2 3 4 5 6 7 // 1234567890123456789012345678901234567890123456789012345678901234567890123456789 Console.WriteLine("RestDb Setup"); Console.WriteLine("------------"); Console.WriteLine("We'll collect some values and put together your initial configuration."); Console.WriteLine(""); Console.WriteLine("On which hostname should this node listen? The hostname supplied here MUST"); Console.WriteLine("match the host header received on incoming RESTful HTTP requests. It is"); Console.WriteLine("recommended that you use the DNS hostname of the machine."); Console.WriteLine(""); Console.WriteLine("Important: if you use localhost or 127.0.0.1, RestDb will only be able to"); Console.WriteLine("accept requests from within the local system. If you use *, +, or 0.0.0.0 to"); Console.WriteLine("represent any address, you will have to run RestDb with admin privileges."); Console.WriteLine(""); ret.Server = new ServerSettings(); ret.Server.ListenerHostname = Common.InputString("Hostname?", "localhost", false); ret.Server.ListenerPort = Common.InputInteger("Port number?", 8000, true, false); ret.Server.Ssl = Common.InputBoolean("Require SSL?", false); ret.Server.Debug = false; ret.Server.ApiKeyHeader = "x-api-key"; Console.WriteLine(""); #endregion #region Database-Settings List <Database> databases = new List <Database>(); while (true) { Console.WriteLine(""); Console.WriteLine("Databases"); Console.WriteLine("---------"); if (databases.Count > 0) { Console.WriteLine("The following databases are configured:"); foreach (Database db in databases) { Console.WriteLine(db.ToString()); } Console.WriteLine(""); } // 0 1 2 3 4 5 6 7 // 1234567890123456789012345678901234567890123456789012345678901234567890123456789 Console.WriteLine("Press ENTER on the database name (blank line) to stop adding databases."); Console.WriteLine(""); Database curr = new Database(); curr.Name = Common.InputString("Database name?", null, true); if (String.IsNullOrEmpty(curr.Name)) { if (databases.Count < 1) { Console.WriteLine("Error: At least one database must be configured."); Console.WriteLine(""); continue; } break; } string currType = Common.InputString("Database type [mssql|mysql|pgsql]?", "mssql", false); if (!currType.Equals("mssql") && !currType.Equals("mysql") && !currType.Equals("pgsql")) { Console.WriteLine("Error: Use mssql, mysql, or pgsql for the database type."); Console.WriteLine(""); continue; } curr.Type = currType; curr.Hostname = Common.InputString("Server hostname?", "localhost", false); if (curr.Type.Equals("mssql")) { curr.Port = Common.InputInteger("Server port?", 1433, true, false); } else if (curr.Type.Equals("mysql")) { curr.Port = Common.InputInteger("Server port?", 3306, true, false); } else { curr.Port = Common.InputInteger("Server port?", 5432, true, false); } curr.Instance = null; if (curr.Type.Equals("mssql")) { curr.Instance = Common.InputString("Instance name?", null, true); } curr.Username = Common.InputString("Username?", null, false); curr.Password = Common.InputString("Password?", null, false); curr.Debug = false; databases.Add(curr); } ret.Databases = databases; Console.WriteLine(""); #endregion #region Finish // 0 1 2 3 4 5 6 7 // 1234567890123456789012345678901234567890123456789012345678901234567890123456789 Console.WriteLine("All set! We're writing your configuration to System.Json. It is important"); Console.WriteLine("to note that, by default, authentication via API key is **DISABLED**. You"); Console.WriteLine("should modify your System.Json file to add API keys to the 'ApiKeys' section."); Console.WriteLine("Then, set 'RequireAuthentication' to true in the 'Server' section."); Console.WriteLine("Once API keys are added and authentication is set to required, requests will"); Console.WriteLine("need to be made including the x-api-key header."); Console.WriteLine(""); ret.ToFile("System.Json"); #endregion }
internal bool Authenticate(HttpContext ctx, out string apiKey, out ApiKey key) { apiKey = null; key = null; #region Extract-API-Key apiKey = ctx.Request.RetrieveHeaderValue(_Settings.Server.ApiKeyHeader); if (String.IsNullOrEmpty(apiKey)) { _Logging.Warn("Authenticate unable to retrieve API key from headers"); return(false); } #endregion #region Compare if (_Keys != null && _Keys.Count > 0) { lock (_KeysLock) { string tempKey = apiKey; if (_Keys.Exists(k => k.Key.Equals(tempKey))) { ApiKey curr = _Keys.Where(k => k.Key.Equals(tempKey)).First(); key = curr; switch (ctx.Request.Method) { case HttpMethod.GET: case HttpMethod.HEAD: return(curr.AllowGet); case HttpMethod.PUT: return(curr.AllowPut); case HttpMethod.POST: return(curr.AllowPost); case HttpMethod.DELETE: return(curr.AllowDelete); default: _Logging.Warn("Authenticate unknown HTTP method " + ctx.Request.Method); return(false); } } } _Logging.Warn("Authenticate unknown API key " + apiKey); return(false); } else { _Logging.Warn("Authenticate no API keys defined in configuration"); return(false); } #endregion }