public RESTReplyData set_public_key(RESTRequestData pReq, List <string> pArgs) { RESTReplyData replyData = new RESTReplyData(); // The HTTP response info ResponseBody respBody = new ResponseBody(); string includedAPIKey = null; string includedPublicKey = null; bool parsed = false; // whether multipart was parsed try { // The PUT sends the key as binary but it is later sent around // and processed as a Base64 string. Stream byteStream = pReq.RequestBodyMultipartStream("public_key"); if (byteStream != null) { includedPublicKey = Tools.ConvertPublicKeyStreamToBase64(byteStream); // There might has been an APIKey includedAPIKey = pReq.RequestBodyMultipart("api_key"); parsed = true; } else { Context.Log.Error("{0} could not extract public key from request body in PUT user/public_key", _logHeader); } } catch (Exception e) { Context.Log.Error("{0} exception parsing multi-part http: {1}", _logHeader, e.ToString()); parsed = false; } if (parsed) { // If there is account authorization in the header, set the account public key. // If no account authorization (the client is not logged in), check for matching // APIkey and then set the session public key. if (String.IsNullOrEmpty(pReq.AuthToken)) { if (includedAPIKey != null && Domains.Instance.TryGetDomainWithAPIKey(includedAPIKey, out DomainEntity aDomain)) { aDomain.Public_Key = includedPublicKey; } else { Context.Log.Error("{0} PUT user/set_public_key requested without APIKey. APIKey={1}", _logHeader, includedAPIKey); respBody.RespondFailure("APIkey does not work"); replyData.Status = (int)HttpStatusCode.Unauthorized; } } else { if (Accounts.Instance.TryGetAccountWithAuthToken(pReq.AuthToken, out AccountEntity aAccount)) { aAccount.Public_Key = includedPublicKey; } else { Context.Log.Error("{0} PUT user/set_public_key requested but could not find acct. Token={1}", _logHeader, pReq.AuthToken); respBody.RespondFailure("AuthToken unauthorized"); replyData.Status = (int)HttpStatusCode.Unauthorized; } } } else { Context.Log.Error("{0} PUT user/set_public_key failure parsing", _logHeader); respBody.RespondFailure("Multi-part body failed parsing"); } replyData.SetBody(respBody, pReq); return(replyData); }
public RESTReplyData user_create(RESTRequestData pReq, List <string> pArgs) { RESTReplyData replyData = new RESTReplyData(); // The HTTP response info ResponseBody respBody = new ResponseBody(); // The request's "data" response info if (Sessions.Instance.ShouldBeThrottled(pReq.SenderKey, Sessions.Op.ACCOUNT_CREATE)) { respBody.RespondFailure("Throttled"); respBody.ErrorData("error", "throttled"); } else { try { bodyUsersPost requestData = pReq.RequestBodyObject <bodyUsersPost>(); string userName = requestData.user["username"]; string userPassword = requestData.user["password"]; string userEmail = requestData.user["email"]; if (CheckUsernameFormat(userName)) { if (CheckEmailFormat(userEmail)) { Context.Log.Debug("{0} Creating account {1}/{2}", _logHeader, userName, userEmail); AccountEntity newAcct = Accounts.Instance.CreateAccount(userName, userPassword, userEmail); if (newAcct == null) { respBody.RespondFailure("Username already exists"); respBody.ErrorData("username", "already exists"); // legacy HiFi compatibility Context.Log.Debug("{0} Failed acct creation. Username already exists. User={1}", _logHeader, userName); } else { // Successful account creation newAcct.IPAddrOfCreator = pReq.SenderKey; newAcct.Updated(); Context.Log.Debug("{0} Successful acct creation. User={1}", _logHeader, userName); } } else { respBody.RespondFailure("Bad format for email"); respBody.ErrorData("error", "bad format for email"); } } else { respBody.RespondFailure("Bad format for username"); respBody.ErrorData("error", "bad format for username"); } } catch (Exception e) { replyData.Status = (int)HttpStatusCode.BadRequest; Context.Log.Error("{0} Badly formed create account request. {1}", _logHeader, e); } } replyData.SetBody(respBody, pReq); return(replyData); }
public RESTReplyData user_connections_request_post(RESTRequestData pReq, List <string> pArgs) { RESTReplyData replyData = new RESTReplyData(); // The HTTP response info ResponseBody respBody = new ResponseBody(); if (Accounts.Instance.TryGetAccountWithAuthToken(pReq.AuthToken, out AccountEntity aAccount)) { bodyUserConnectionRequestPost request = pReq.RequestBodyObject <bodyUserConnectionRequestPost>(); // The script looks for two types of 'connection' responses. // If is sees data.connection == "pending", it trys again and eventually times out // If data.connection has an object, it uses 'new_connection' and 'username' // Connection handles are the in-world nodeId's string thisNode = request.user_connection_request.node_id; string otherNode = request.user_connection_request.proposed_node_id; // BEGIN sanity check DEBUG DEBUG { // Connections use node id's to identify the two avatars, I guess because, the // world does not have access to the account identity. // This debugging code prints out whether the passed nodeIds match the loction // nodeIds that we have to verify that the connection nodeIds are really the // same ones as passed in the location. // All this debugging output can go away once nodeId usage is understood. Context.Log.Debug("{0} connection_request: request from account '{1}' node={2}, proposed={3}", _logHeader, aAccount.Username, thisNode, otherNode); if (aAccount.Location != null) { if (aAccount.Location.NodeID == thisNode) { Context.Log.Debug("{0} connection_request: request from account '{1}' and Location.NodeId matches main node", _logHeader, aAccount.Username); } else { if (aAccount.Location.NodeID == otherNode) { Context.Log.Debug("{0} connection_request: request from account '{1}' and Location.NodeId matches proposed node", _logHeader, aAccount.Username); } else { Context.Log.Debug("{0} connection_request: request from account '{1}' and Location.NodeId does not match either nodes", _logHeader, aAccount.Username); } } } else { Context.Log.Debug("{0} connection_request: request from account '{1}' and no location info", _logHeader, aAccount.Username); } } // END sanity check DEBUG DEBUG try { // This returns all the RequestConnection's that have my ID in them RequestConnection previousAsk = RequestConnection.GetNodeRequests(thisNode) // Find the ones that have me and the other requestor .Where(req => { return((req.Body.node_id == thisNode && req.Body.proposed_node_id == otherNode) || (req.Body.node_id == otherNode && req.Body.proposed_node_id == thisNode)); // Pick the one (there should be only one) }).FirstOrDefault(); if (previousAsk != null) { Context.Log.Debug("{0} connection_request: existing request found", _logHeader); // We have a pending connection request for this person // Mark myself as accepting if (previousAsk.Body.node_id == thisNode) { if (!previousAsk.Body.node_accepted) { previousAsk.Body.node_accepted = true; previousAsk.Updated(); } } else { if (!previousAsk.Body.proposed_node_accepted) { previousAsk.Body.proposed_node_accepted = true; previousAsk.Updated(); } // I'm in the 'proposed node' position. // Straighten out the node variables for simplier logic after this var tmp = otherNode; otherNode = thisNode; thisNode = tmp; // after this, "thisNode" is always this account Context.Log.Debug("{0} connection_request: found request was from other account", _logHeader); } // If both accepted, the connection is complete if (previousAsk.Body.node_accepted && previousAsk.Body.proposed_node_accepted) { if (Accounts.Instance.TryGetAccountWithNodeId(otherNode, out AccountEntity oOtherAccount)) { aAccount.AddConnection(oOtherAccount); aAccount.Updated(); oOtherAccount.AddConnection(aAccount); oOtherAccount.Updated(); respBody.Data = new { connection = new { new_connection = true, // says whether a new or pre-existing connection username = oOtherAccount.Username } }; } else { // Can't find the other account... shouldn't happen respBody.RespondFailure("Both agreed but cannot find other account entity"); } } else { // Both haven't accepted so tell the requestor that the request is pending respBody.Data = new Dictionary <string, string>() { { "connection", "pending" } }; } } else { // There was no previous ask so create the request. Context.Log.Debug("{0} connection_request: creating connection request from '{1}", _logHeader, aAccount.Username); RequestConnection newReq = new RequestConnection(); newReq.Body.node_id = thisNode; newReq.Body.requestor_accountId = aAccount.AccountID; newReq.Body.proposed_node_id = otherNode; newReq.Body.node_accepted = true; newReq.ExpireIn(TimeSpan.FromSeconds(Context.Params.P <int>(AppParams.P_CONNECTION_REQUEST_SECONDS))); newReq.AddAndUpdate(); // Both haven't accepted so tell the requestor that the request is pending respBody.Data = new Dictionary <string, string>() { { "connection", "pending" } }; } } catch { } } else { Context.Log.Error("{0} POST user/connection_request for unauthorized user. Token={1}", _logHeader, pReq.AuthToken); respBody.RespondFailure("Unauthorized"); } replyData.SetBody(respBody, pReq); return(replyData); }
public RESTReplyData domain_heartbeat(RESTRequestData pReq, List <string> pArgs) { RESTReplyData replyData = new RESTReplyData(); // The HTTP response info ResponseBody respBody = new ResponseBody(); // The request's "data" response info SessionEntity aSession = Sessions.Instance.UpdateSession(pReq.SenderKey); string domainID = pArgs.Count == 1 ? pArgs[0] : null; if (Domains.Instance.TryGetDomainWithID(domainID, out DomainEntity aDomain)) { try { // Since the body has a lot of optional fields, we need to pick // through what's sent so we only set what was sent. JObject requestData = pReq.RequestBodyJSON(); JObject domainStuff = (JObject)requestData["domain"]; string apiKey = (string)domainStuff["api_key"]; // Context.Log.Debug("{0} domain_heartbeat. Received {1}", _logHeader, pReq.RequestBody); if (VerifyDomainAccess(aDomain, pReq, apiKey, out string oFailureReason)) { Tools.SetIfSpecified <string>(domainStuff, "version", ref aDomain.Version); Tools.SetIfSpecified <string>(domainStuff, "protocol", ref aDomain.Protocol); Tools.SetIfSpecified <string>(domainStuff, "network_address", ref aDomain.NetworkAddr); Tools.SetIfSpecified <string>(domainStuff, "automatic_networking", ref aDomain.NetworkingMode); Tools.SetIfSpecified <bool>(domainStuff, "restricted", ref aDomain.Restricted); Tools.SetIfSpecified <int>(domainStuff, "capacity", ref aDomain.Capacity); Tools.SetIfSpecified <string>(domainStuff, "description", ref aDomain.Description); Tools.SetIfSpecified <string>(domainStuff, "maturity", ref aDomain.Maturity); Tools.SetIfSpecified <string>(domainStuff, "restriction", ref aDomain.Restriction); JArray hosts = (JArray)domainStuff["hosts"]; if (hosts != null) { aDomain.Hosts = domainStuff["hosts"].ToObject <string[]>(); } JArray tags = (JArray)domainStuff["tags"]; if (tags != null) { aDomain.Tags = domainStuff["tags"].ToObject <string[]>(); } JObject heartbeat = (JObject)domainStuff["heartbeat"]; if (heartbeat != null) { Tools.SetIfSpecified <int>(heartbeat, "num_users", ref aDomain.NumUsers); Tools.SetIfSpecified <int>(heartbeat, "num_anon_users", ref aDomain.AnonUsers); aDomain.TotalUsers = aDomain.NumUsers + aDomain.AnonUsers; // TODO: what to do with user_hostnames } aDomain.TimeOfLastHeartbeat = DateTime.UtcNow; aDomain.LastSenderKey = pReq.SenderKey; aDomain.Updated(); } else { Context.Log.Error("{0} domain_heartbeat with bad authorization. domainID={1}", _logHeader, domainID); respBody.RespondFailure(oFailureReason); replyData.Status = (int)HttpStatusCode.Unauthorized; } } catch (Exception e) { Context.Log.Error("{0} domain_heartbeat: Exception processing body: {1}", _logHeader, e.ToString()); respBody.RespondFailure("exception processing body", e.ToString()); replyData.Status = (int)HttpStatusCode.BadRequest; } } else { Context.Log.Error("{0} domain_heartbeat: unknown domain. Returning 404", _logHeader); respBody.RespondFailure("no such domain"); replyData.Status = (int)HttpStatusCode.NotFound; // this will trigger a new temporary domain name } replyData.SetBody(respBody, pReq); return(replyData); }