private errordetail ValidateParameters(FormBodyParser form) { errordetail result = null; // @"soundId=%@&rating=%d&text=%@" if (!Functions.IsNumeric(form.Value(QsKeys.SoundId))) { result = new errordetail("Value for soundid must be numeric.", System.Net.HttpStatusCode.BadRequest); } else if (!Functions.IsNumeric(form.Value(QsKeys.Rating))) { result = new errordetail("Value for rating must be numeric.", System.Net.HttpStatusCode.BadRequest); } else { int rating = Functions.ConvertInt(form.Value(QsKeys.Rating), -1); if (rating <= 0 || rating >= 6) { result = new errordetail("Value for rating must be 1 through 5.", System.Net.HttpStatusCode.BadRequest); } } return(result); }
private status RecordStorePurchase(Stream postBody) { // // Validate the request. // RequestValidation.Validate(); // // Get the passed data POSTed to the service as a form. // FormBodyParser form = new FormBodyParser(postBody); errordetail validationError = ValidateParameters(form); if (validationError != null) { throw new WebFaultException <errordetail>(validationError, validationError.statuscode); } // // With the passed values, let's make it so. // bool res = DataManager.RecordPurchase(form.Value(QsKeys.PurchaseId), form.Value(QsKeys.DeviceId), form.Value(QsKeys.AppVersion)); if (res) { return(new status(ResultStatus.Success)); } else { return(new status(ResultStatus.Error)); } }
/// <summary> /// From the current context, determine if we have enough information to authenticate the user/request. /// </summary> /// <param name="opRequiresCredentials">Whether the operation requires credentials or not.</param> /// <param name="auth">An authentication configuration instance.</param> /// <param name="context">The server request context.</param> /// <param name="operationAuthenticationMethod">The authentication method required for the current operation.</param> /// <param name="authModule">(out) A authentication module that can be used to authenticate this user.</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail ValidateAuthenticationCredentials(bool opRequiresCredentials, AuthenticationConfig auth, System.ServiceModel.Web.IncomingWebRequestContext context, AuthenticationMethod operationAuthenticationMethod, out OttaMattaAuthentication authModule) { errordetail result = null; authModule = null; if (opRequiresCredentials) { if (operationAuthenticationMethod == AuthenticationMethod.Basic) { authModule = new BasicAuthentication(); } else { authModule = new DigestAuthentication(); } authModule.Context = context; authModule.Auth = auth; result = authModule.Authenticate(); } return(result); }
private OttaMatta.Data.Models.websearch GetSummary(string term, string clientIp, string deviceId, string appVersion) { RequestValidation.Validate(); if (Functions.IsEmptyString(term)) { errordetail err = new errordetail("No search term present", System.Net.HttpStatusCode.BadRequest); throw new WebFaultException <errordetail>(err, err.statuscode); } if (Functions.IsEmptyString(clientIp)) { clientIp = ApplicationManager.GetUserIPFromOperationContect(OperationContext.Current); } /* Google version * OttaMatta.Data.Models.websearch result = WebSearchManager.SearchSounds(term, * clientIp, * new DataSourceFileSystem(HttpContext.Current.Server.MapPath(Config.Get(Config.CacheSearchesDirectory))), * new ExternalSearchGoogle()); */ /* Yahoo version */ OttaMatta.Data.Models.websearch result = WebSearchManager.SearchSounds(term, clientIp, new DataSourceFileSystem(HttpContext.Current.Server.MapPath(Config.Get(Config.CacheSearchesDirectory)), HttpContext.Current.Server.MapPath(Config.Get(Config.CacheWebobjectsDirectory))), new ExternalSearchYahoo()); return(result); }
/// <summary> /// From the operation, determine if we need to get credentials and if so, what authentication method is required. /// </summary> /// <param name="auth">An authentication configuration instance.</param> /// <param name="operation">The operation that is being requested.</param> /// <param name="opRequiresCredentials">(out) True if this operation requires credentials.</param> /// <param name="operationAuthenticationMethod">(out) The authentication method required by this operation, or "Unknown".</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail GetCredentialStatus(AuthenticationConfig auth, string operation, out bool opRequiresCredentials, out AuthenticationMethod operationAuthenticationMethod) { errordetail result = null; operationAuthenticationMethod = AuthenticationMethod.Unknown; opRequiresCredentials = true; // // Test if the operation requires credentials. If so, authenticate and authorize. // if (auth.OperationAuthenticationMethods.ContainsKey(operation)) { operationAuthenticationMethod = auth.OperationAuthenticationMethods[operation]; } if (operationAuthenticationMethod == AuthenticationMethod.Unknown) { result = new errordetail("The operation's authentication scheme is not configured properly on the server.", HttpStatusCode.InternalServerError); } else { opRequiresCredentials = (operationAuthenticationMethod != AuthenticationMethod.None); } return(result); }
/// <summary> /// Determine if the request is within throttling limits on this server. /// </summary> /// <param name="authModule">The authentication module instance to use to authenticate the user for the operation.</param> /// <param name="operation">The operation that is being requested.</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail ValidateThrottleForUser(OttaMattaAuthentication authModule, string operation) { errordetail result = null; // // Todo: apply throttling limits // return(result); }
/// <summary> /// Validate the passed form for values /// </summary> /// <param name="form"></param> /// <returns></returns> private errordetail ValidateParameters(FormBodyParser form) { errordetail result = null; if (!Functions.IsNumeric(form.Value(QsKeys.SoundId))) { result = new errordetail("Value for soundid must be numeric.", System.Net.HttpStatusCode.BadRequest); } return(result); }
/// <summary> /// For the request, determine if the requestor's IP address is valid. /// </summary> /// <param name="auth">An authentication configuration instance.</param> /// <param name="authModule">The authentication module instance to use to authenticate the user for the operation.</param> /// <param name="operation">The operation that is being requested.</param> /// <param name="opContext">The OperationContext for the request (note: this is not the WebOperationContext used for other calls).</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail AuthorizeIPAddressForUser(AuthenticationConfig auth, OttaMattaAuthentication authModule, string operation, System.ServiceModel.OperationContext opContext) { errordetail result = null; const string endpointPropertyName = "System.ServiceModel.Channels.RemoteEndpointMessageProperty"; bool failIfNoIP = false; System.ServiceModel.Channels.MessageProperties properties = opContext.IncomingMessageProperties; if (properties.ContainsKey(endpointPropertyName)) { RemoteEndpointMessageProperty endpoint = properties[endpointPropertyName] as RemoteEndpointMessageProperty; // // Note: on local dev machines this is the IPv6 address, like "fe80::c513:967d:1b57:8c33%11". // string ipAddress = endpoint.Address; // // We have an IP address. Let's see if it's authorized for this user. // if (authModule != null && auth.UserAllowedIps.ContainsKey(authModule.Username)) { bool ipOK = auth.UserAllowedIps[authModule.Username].Contains(ipAddress); if (!ipOK) { result = new errordetail(string.Format("The IP address \"{0}\" is not authorized for user account \"{1}\"", ipAddress, authModule.Username), HttpStatusCode.Forbidden); } } else { // // There are no IP address restrictions for this account. // } } else { // // Can't get the remote IP from the system. We can assume that something has changed in the WCF .NET codebase, and allow it for now. // Or, we can fail the request. // if (failIfNoIP) { result = new errordetail("Unable to determine request IP address", HttpStatusCode.InternalServerError); } } return(result); }
/// <summary> /// Process the upload request. /// </summary> /// <param name="postBody">The upload data POSTed to the service</param> /// <returns>The inserted sound if successful, otherwise throws an eror. Just the new id is populated.</returns> private sound UploadASound(Stream postBody) { // // Validate the request. // RequestValidation.Validate(); // // Get the passed data POSTed to the service as a form. // FormBodyParser form = new FormBodyParser(postBody); errordetail validationError = ValidateParameters(form); if (validationError != null) { throw new WebFaultException <errordetail>(validationError, validationError.statuscode); } // // With the passed values, let's make it so. // int newId = -1; bool res = DataManager.InsertSound(form.Value(QsKeys.Name), form.Value(QsKeys.SoundFName), form.Value(QsKeys.Description), form.Value(QsKeys.UserId), form.Base64DecodedValue(QsKeys.SoundData), form.Value(QsKeys.IconFName), form.Base64DecodedValue(QsKeys.IconData), Functions.ConvertBool(form.Value(QsKeys.IsBrowsable), false), out newId ); //bool res = false; if (!res) { throw new WebFaultException <errordetail>(new errordetail("Error saving sound", System.Net.HttpStatusCode.InternalServerError), System.Net.HttpStatusCode.InternalServerError); } sound newSound = new sound(); newSound.soundid = newId; return(newSound); }
/// <summary> /// From the operation, determine if SSL is required and if so, if the current request uses it. /// </summary> /// <param name="auth">An authentication configuration instance.</param> /// <param name="operation">The operation that is being requested.</param> /// <param name="isSSL">Whether the current operation is SSL or not.</param> /// <param name="operationAuthenticationMethod">The authentication method required for the current operation.</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail ValidateSSLStatus(AuthenticationConfig auth, string operation, bool isSSL, AuthenticationMethod operationAuthenticationMethod) { errordetail result = null; // // Test if the operation requires SSL. // bool requiresSSL = auth.OperationsRequiringSSL.Contains(operation) || (operationAuthenticationMethod == AuthenticationMethod.Basic && !auth.OperationsRequiringBasicAuthButAllowingNoSSL.Contains(operation)); if (requiresSSL && !isSSL) { result = new errordetail(string.Format("The operation \"{0}\" requires a secure connection.", operation), HttpStatusCode.BadRequest); } return(result); }
/// <summary> /// From the initial web request, extract some basic information. /// </summary> /// <param name="context">The web request context.</param> /// <param name="headers">(out) The headers of the request.</param> /// <param name="operation">(out) The operation requested.</param> /// <param name="isSSL">(out) True if the operation is using SSL.</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail GatherBasicInfo(System.ServiceModel.Web.IncomingWebRequestContext context, out WebHeaderCollection headers, out string operation, out bool isSSL) { errordetail result = null; headers = context.Headers; int lastSlashPos = context.UriTemplateMatch.BaseUri.AbsolutePath.LastIndexOf('/') + 1; operation = context.UriTemplateMatch.BaseUri.AbsolutePath.Substring(lastSlashPos); // string.Empty; isSSL = context.UriTemplateMatch.BaseUri.Scheme == "https"; if (Functions.IsEmptyString(operation)) { result = new errordetail(string.Format("Unable to determine operation: \"{0}\"", operation), HttpStatusCode.BadRequest); } return(result); }
/// <summary> /// Validate the passed form for values /// </summary> /// <param name="form">The passed data in a form parser</param> /// <returns>The error that happened, or null.</returns> private errordetail ValidateParameters(FormBodyParser form) { errordetail result = null; string missingValue = form.CheckNonEmptyParams(new Dictionary <string, string>() { { QsKeys.Name, "sound name" }, { QsKeys.SoundFName, "sound filename" }, { QsKeys.Description, "sound description" }, { QsKeys.UserId, "user name" }, { QsKeys.SoundData, "sound data" }, { QsKeys.SoundDataMd5, "sound checksum" }, { QsKeys.IconFName, "icon filename" }, { QsKeys.IconData, "icon data" }, { QsKeys.IconDataMd5, "icon checksum" }, { QsKeys.IsBrowsable, "is browsable" } }, form.CommonEmptyValues); if (!Functions.IsEmptyString(missingValue)) { result = new errordetail(string.Format("Value for {0} is missing.", missingValue), System.Net.HttpStatusCode.BadRequest); } else { byte[] soundData = form.Base64DecodedValue(QsKeys.SoundData); byte[] iconData = form.Base64DecodedValue(QsKeys.IconData); if (soundData == null || Functions.GetMd5Hash(soundData) != form.Value(QsKeys.SoundDataMd5)) { result = new errordetail(string.Format("Sound data not received properly."), System.Net.HttpStatusCode.BadRequest); } else if (iconData == null || Functions.GetMd5Hash(iconData) != form.Value(QsKeys.IconDataMd5)) { result = new errordetail(string.Format("Icon data not received properly."), System.Net.HttpStatusCode.BadRequest); } } // // Note: we could check for max value lengths here, but eh, let the stored proc bomb out. The client // should check for lengths // return(result); }
/// <summary> /// Validate the passed form for values /// </summary> /// <param name="form"></param> /// <returns></returns> private errordetail ValidateParameters(FormBodyParser form) { errordetail result = null; // form.Value("purchaseId"), form.Value("userId") if (Functions.IsEmptyString(form.Value(QsKeys.PurchaseId))) { result = new errordetail("Value for purchase id is missing.", System.Net.HttpStatusCode.BadRequest); } else if (Functions.IsEmptyString(form.Value(QsKeys.DeviceId))) { result = new errordetail("Value for device id is missing.", System.Net.HttpStatusCode.BadRequest); } else if (Functions.IsEmptyString(form.Value(QsKeys.AppVersion))) { result = new errordetail("Value for app version is missing.", System.Net.HttpStatusCode.BadRequest); } return(result); }
/// <summary> /// Process the web request /// </summary> /// <param name="postBody">The request parameters</param> /// <returns>The status or an error is thrown.</returns> private status ProcessSoundRating(Stream postBody) { // // Validate the request. // RequestValidation.Validate(); // // Get the passed data POSTed to the service as a form. // FormBodyParser form = new FormBodyParser(postBody); errordetail validationError = ValidateParameters(form); if (validationError != null) { throw new WebFaultException <errordetail>(validationError, validationError.statuscode); } // // With the passed values, let's make it so. // bool res = DataManager.RateSound(int.Parse(form.Value(QsKeys.SoundId)), int.Parse(form.Value(QsKeys.Rating)), form.Value(QsKeys.Text)); return(new status { code = 0, description = "Success" }); /* * else * { * errordetail err = new errordetail("Data update failed.", System.Net.HttpStatusCode.InternalServerError); * throw new WebFaultException<errordetail>(err, err.statuscode); * } * */ }
private OttaMatta.Data.Models.websearchsound GetSound(string term, string soundId, string deviceId, string appVersion) { RequestValidation.Validate(); if (Functions.IsEmptyString(soundId)) { errordetail err = new errordetail("No sound id passed", System.Net.HttpStatusCode.BadRequest); throw new WebFaultException <errordetail>(err, err.statuscode); } OttaMatta.Data.Models.websearchsound result = WebSearchManager.GetSound(term, soundId, new DataSourceFileSystem(HttpContext.Current.Server.MapPath(Config.Get(Config.CacheSearchesDirectory)), HttpContext.Current.Server.MapPath(Config.Get(Config.CacheWebobjectsDirectory)))); if (result == null) { errordetail err = new errordetail("Sound id not found", System.Net.HttpStatusCode.BadRequest); throw new WebFaultException <errordetail>(err, err.statuscode); } else { return(result); } }
/// <summary> /// For the request, get the credentials (if required) and determine if the user is authorized. /// </summary> /// <param name="opRequiresCredentials">Whether the operation requires credentials or not.</param> /// <param name="auth">An authentication configuration instance.</param> /// <param name="authModule">The authentication module instance to use to authenticate the user for the operation.</param> /// <param name="operation">The operation that is being requested.</param> /// <returns>An errordetail instance holding any error that was encountered, or null if no errors were encountered.</returns> private static errordetail AuthorizeUserForOperation(bool opRequiresCredentials, AuthenticationConfig auth, OttaMattaAuthentication authModule, string operation) { errordetail result = null; if (opRequiresCredentials) { // // UID and PW good. Authorize the user for this operation // bool userAllowed = false; if (auth.UserAllowedOperations.ContainsKey(authModule.Username)) { userAllowed = auth.UserAllowedOperations[authModule.Username].Contains(operation); } if (!userAllowed) { result = new errordetail(string.Format("Operation \"{0}\" not authorized for user \"{1}\"", operation, authModule.Username), HttpStatusCode.Forbidden); } } return(result); }
/// <summary> /// Authenticate the request. /// </summary> /// <returns>An errodetail instance holding what went wrong, or null is the request is valid.</returns> /// <remarks> /// If the operation is not authorized, this function adds the appropriate headers to the response to request credentials from the client. /// </remarks> public override errordetail Authenticate() { errordetail currentException = null; WebHeaderCollection headers = Context.Headers; string authorizationHeader = headers["Authorization"]; string username = string.Empty; string password = string.Empty; if (authorizationHeader != null && authorizationHeader.Trim() != string.Empty) { // // Determine the beginning index of the Base64-encoded string in the Authorization header by finding the first space. // Add 1 to the index so we can properly grab the substring. // var beginPasswordIndexPosition = authorizationHeader.IndexOf(' ') + 1; var encodedAuth = authorizationHeader.Substring(beginPasswordIndexPosition); try { // // Decode the authentication credentials. This can bomb out, so let's use a try/catch. // var decodedAuth = Encoding.UTF8.GetString(Convert.FromBase64String(encodedAuth)); // Split the credentials into the username and password portions on the colon character. string[] splits = decodedAuth.Split(':'); if (splits.Length == 2) { username = splits[0]; password = splits[1]; } else { currentException = ResponseCredentialsPresentButCantDecode; } // // Authenticate the user // string requiredPassword = string.Empty; if (Auth.Users.ContainsKey(username)) { requiredPassword = Auth.Users[username]; } else { currentException = ResponseCredentialsReceivedButUnknownUser; } if (currentException == null) { if (password.CompareTo(requiredPassword) != 0) { currentException = ResponseCredentialsReceivedButBadPassword; } else { // // Success. The user is found and valid. // Username = username; } } } catch { AuthenticationMethod requestAuthMethod = PassedCredentialsAuthMethod(headers); if (requestAuthMethod != AuthenticationMethod.Basic) { AddAuthenticationHeader(); currentException = ResponseCredentialsReceivedIncorrectType; } else { currentException = ResponseCredentialsPresentButCantDecode; } } } else { AddAuthenticationHeader(); currentException = ResponseNoCredentialsFoundInRequest; } return(currentException); }
/// <summary> /// Authenticate the user request. /// </summary> /// <returns>Returns the error object if there's an issue, otherwise returns null</returns> /// <remarks> /// If the operation is not authorized, this function adds the appropriate headers to the response to request credentials from the client. /// </remarks> public override errordetail Authenticate() { errordetail currentException = null; WebHeaderCollection headers = Context.Headers; string authStr = headers["Authorization"]; if (string.IsNullOrEmpty(authStr)) { // // No credentials. Typical for a first-request. Send back a 401 so the client can respond with credentials. // Set401AuthenticationHeaders(Context, false); return(ResponseNoCredentialsFoundInRequest); } authStr = authStr.Substring(7); ListDictionary reqInfo = new ListDictionary(); string[] elems = authStr.Split(new char[] { ',' }); foreach (string elem in elems) { // form key="value" string[] parts = elem.Split(new char[] { '=' }, 2); if (parts.Length > 1) { string key = parts[0].Trim(new char[] { ' ', '\"' }); string val = parts[1].Trim(new char[] { ' ', '\"' }); reqInfo.Add(key, val); } } string username = (string)reqInfo["username"]; string password = ""; // // Get the password based upon the username. // bool bOk = username != null && Auth.GetPassword(username, out password); if (!bOk) { // // Username not found. // return(ResponseCredentialsReceivedButUnknownUser); } //get the realm from the config file string realm = Realm; // calculate the Digest hashes // A1 = unq(username-value) ":" unq(realm-value) ":" passwd string A1 = String.Format("{0}:{1}:{2}", (string)reqInfo["username"], realm, password); // H(A1) = MD5(A1) string HA1 = GetMD5HashBinHex(A1); // A2 = Method ":" digest-uri-value string A2 = String.Format("{0}:{1}", Context.Method, (string)reqInfo["uri"]); // JB: app.Request.HttpMethod // H(A2) string HA2 = GetMD5HashBinHex(A2); string unhashedDigest; if (reqInfo["qop"] != null) { unhashedDigest = String.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, (string)reqInfo["nonce"], (string)reqInfo["nc"], (string)reqInfo["cnonce"], (string)reqInfo["qop"], HA2); } else { unhashedDigest = String.Format("{0}:{1}:{2}", HA1, (string)reqInfo["nonce"], HA2); } string hashedDigest = GetMD5HashBinHex(unhashedDigest); bool isNonceStale = !IsValidNonce((string)reqInfo["nonce"]); // // If the result of their hash is equal to the result of our hash, then this // is a valid request from a partner // if (isNonceStale) { Set401AuthenticationHeaders(Context, true); return(ResponseDigestStaleNonce); } else if ((string)reqInfo["response"] != hashedDigest) { Set401AuthenticationHeaders(Context, false); return(ResponseCredentialsReceivedButBadPassword); } else { // // We're golden! // Username = username; } return(currentException); }
/// <summary> /// Validate whether the user is authenticated and whether the user is authorized for this operation. /// </summary> /// <remarks> /// This function will not return if the request is invalid. An error is thrown and the appropriate response returned to the client. /// If this function does return, the request can safely be considered valid. /// </remarks> public static void Validate() { // // Variables required for authentication and authorization. // System.ServiceModel.Web.IncomingWebRequestContext context = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest; errordetail currentException = null; AuthenticationConfig auth = AuthenticationConfig.Instance; WebHeaderCollection headers = null; string ipAddress = string.Empty; string operation = string.Empty; bool isSSL = false; AuthenticationMethod operationAuthenticationMethod = AuthenticationMethod.Unknown; bool opRequiresCredentials = true; OttaMattaAuthentication authModule = null; // // Test for credentials // currentException = GatherBasicInfo(context, out headers, out operation, out isSSL); if (currentException == null) { currentException = GetCredentialStatus(auth, operation, out opRequiresCredentials, out operationAuthenticationMethod); } // // Validate SSL status // if (currentException == null) { currentException = ValidateSSLStatus(auth, operation, isSSL, operationAuthenticationMethod); } // // Authenticate the user // if (currentException == null) { currentException = ValidateAuthenticationCredentials(opRequiresCredentials, auth, context, operationAuthenticationMethod, out authModule); } // // Authorize the user for this operation // if (currentException == null) { currentException = AuthorizeUserForOperation(opRequiresCredentials, auth, authModule, operation); } // // Validate IP address // if (currentException == null) { currentException = AuthorizeIPAddressForUser(auth, authModule, operation, System.ServiceModel.OperationContext.Current); } // // Validate throttling // if (currentException == null) { currentException = ValidateThrottleForUser(authModule, operation); } // // Can't continue - return the response. // if (currentException != null) { throw new WebFaultException <errordetail>(currentException, currentException.statuscode); } }