/// <summary> /// Attempts to generate the key and returns the result. /// </summary> /// <param name="client">The remote client.</param> /// <param name="channel">The full channel string.</param> /// <param name="message">The message to publish.</param> public static EmitterResponse Process(IClient client, EmitterChannel channel, KeyGenRequest request) { // Parse the channel EmitterChannel info; if (!EmitterChannel.TryParse(request.Channel, false, out info)) { return(EmitterError.BadRequest); } // Should be a static (non-wildcard) channel string. if (info.Type != ChannelType.Static) { return(EmitterError.BadRequest); } // Attempt to parse the key, this should be a master key SecurityKey masterKey; if (!SecurityKey.TryParse(request.Key, out masterKey) || !masterKey.IsMaster || masterKey.IsExpired) { return(EmitterError.Unauthorized); } // Attempt to fetch the contract using the key. Underneath, it's cached. var contract = Services.Contract.GetByKey(masterKey.Contract) as EmitterContract; if (contract == null) { return(EmitterError.NotFound); } // Validate the contract if (!contract.Validate(ref masterKey)) { return(EmitterError.Unauthorized); } // Generate the key var key = SecurityKey.Create(); key.Master = masterKey.Master; key.Contract = contract.Oid; key.Signature = contract.Signature; key.Permissions = request.Access; key.Target = SecurityHash.GetHash(request.Channel); key.Expires = request.Expires; return(new KeyGenResponse() { Key = key.Value, Channel = request.Channel }); }
/// <summary> /// Attempts to publish and returns the result. /// </summary> /// <param name="client">The remote client.</param> /// <param name="channel">The full channel string.</param> /// <param name="message">The message to publish.</param> public static EmitterEventCode Process(IClient client, string channel, ArraySegment <byte> message) { try { // Parse the channel EmitterChannel info; if (!EmitterChannel.TryParse(channel, true, out info)) { return(EmitterEventCode.BadRequest); } // Is this a special api request? if (HandleRequest.TryProcess(client, info, message)) { return(EmitterEventCode.Success); } // Publish should only have static channel strings if (info.Type != ChannelType.Static) { return(EmitterEventCode.Forbidden); } // Attempt to parse the key SecurityKey key; if (!SecurityKey.TryParse(info.Key, out key)) { return(EmitterEventCode.BadRequest); } // Has the key expired? if (key.IsExpired) { return(EmitterEventCode.Unauthorized); } // Attempt to fetch the contract using the key. Underneath, it's cached. var contract = Services.Contract.GetByKey(key.Contract) as EmitterContract; if (contract == null) { return(EmitterEventCode.NotFound); } // Check if the payment state is valid if (contract.Status == EmitterContractStatus.Refused) { return(EmitterEventCode.PaymentRequired); } // Validate the contract if (!contract.Validate(ref key)) { return(EmitterEventCode.Unauthorized); } // Check if the key has the permission to write here if (!key.HasPermission(SecurityAccess.Write)) { return(EmitterEventCode.Unauthorized); } // Check if the key has the permission for the required channel if (key.Target != 0 && info.Target != key.Target) { return(EmitterEventCode.Unauthorized); } // Do we have a TTL with the message? int ttl; info.HasTimeToLive(out ttl); // Check if the key has a TTL and also can store (soft permission) if (ttl > 0 && !key.HasPermission(SecurityAccess.Store)) { ttl = 0; } // Publish within the service Dispatcher.Publish(contract, info.Target, info.Channel, message, ttl); // Successfully published return(EmitterEventCode.Success); } catch (NotImplementedException) { // We've got a not implemented exception return(EmitterEventCode.NotImplemented); } catch (Exception ex) { // We need to log it Service.Logger.Log(ex); // We've got a an internal error return(EmitterEventCode.ServerError); } }
/// <summary> /// Occurs when the remote client attempts to subscribe to a hub. /// </summary> /// <param name="client">The remote client.</param> /// <param name="channel">The full channel string.</param> public static EmitterEventCode Process(IClient client, string channel) { try { // Parse the channel EmitterChannel info; if (!EmitterChannel.TryParse(channel, true, out info)) { return(EmitterEventCode.BadRequest); } // Simple ACK for api subscribe. We don't really need to subscribe as // this uses request/response topology and hence the response is sent // through the same TCP connection. if (info.Key == "emitter") { return(EmitterEventCode.Success); } // Attempt to parse the key SecurityKey key; if (!SecurityKey.TryParse(info.Key, out key)) { return(EmitterEventCode.BadRequest); } // Has the key expired? if (key.IsExpired) { return(EmitterEventCode.Unauthorized); } // Have we already subscribed? //if (client[channel] != null) // return EmitterEventCode.Success; // Attempt to fetch the contract using the key. Underneath, it's cached. var contract = Services.Contract.GetByKey(key.Contract) as EmitterContract; if (contract == null) { return(EmitterEventCode.NotFound); } // Check if the payment state is valid if (contract.Status == EmitterContractStatus.Refused) { return(EmitterEventCode.PaymentRequired); } // Validate the contract if (!contract.Validate(ref key)) { return(EmitterEventCode.Unauthorized); } // Check if the key has the permission to read here if (!key.HasPermission(SecurityAccess.Read)) { return(EmitterEventCode.Unauthorized); } // Check if the key has the permission for the required channel if (key.Target != 0 && info.Target != key.Target) { return(EmitterEventCode.Unauthorized); } // Subscribe to the channel var subs = Dispatcher.Subscribe(client, key.Contract, info.Channel, SubscriptionInterest.Messages); // Check if the history was also requested and we have the permission to do so var last = 0; if (!info.RequestedLast(out last) || !key.HasPermission(SecurityAccess.Load)) { return(EmitterEventCode.Success); } // Get the ssid var ssid = EmitterChannel.Ssid(key.Contract, info.Channel); // Stream the history Services.Storage .GetLastAsync(key.Contract, ssid, last) .ContinueWith(async(t) => { // Now send each message in order var stream = t.Result; while (stream.HasNext) { // Get the message asyncronously var item = await stream.GetNext(); if (item.Count == 0) { continue; } // Increment the counter Service.MessageSent?.Invoke(contract, channel, item.Count); // Send the message out var msg = MqttPublishPacket.Acquire(); msg.Channel = info.Channel; msg.Message = item; client.Send(msg); } }); // We have successfully subscribed return(EmitterEventCode.Success); } catch (NotImplementedException) { // We've got a not implemented exception return(EmitterEventCode.NotImplemented); } catch (Exception ex) { // We need to log it Service.Logger.Log(ex); // We've got a an internal error return(EmitterEventCode.ServerError); } }
/// <summary> /// Attempts to generate the key and returns the result. /// </summary> /// <param name="client">The remote client.</param> /// <param name="channel">The full channel string.</param> /// <param name="message">The message to publish.</param> public static EmitterResponse Process(IClient client, EmitterChannel channel, PresenceRequest request) { // Parse the channel EmitterChannel info; if (!EmitterChannel.TryParse(request.Channel, false, out info)) { return(EmitterError.BadRequest); } // Should be a static (non-wildcard) channel string. if (info.Type != ChannelType.Static) { return(EmitterError.BadRequest); } // Attempt to parse the key, this should be a master key SecurityKey channelKey; if (!SecurityKey.TryParse(request.Key, out channelKey) || !channelKey.HasPermission(SecurityAccess.Presence) || channelKey.IsExpired) { return(EmitterError.Unauthorized); } // Make sure the channel name ends with if (!request.Channel.EndsWith("/")) { request.Channel += "/"; } // Subscription Subscription sub; if (request.Changes) { // If we requested changes, register a subscription with this new interest Subscription.Register(client, channelKey.Contract, request.Channel, SubscriptionInterest.Presence); } else { // If the changes flag is set to false, unregister the client from presence Subscription.Unregister(client, channelKey.Contract, request.Channel, SubscriptionInterest.Presence, out sub); } // If we didn't request a presence, send an OK response only if (!request.Status) { return(new EmitterResponse(200)); } // Find the subscription if (!Subscription.TryGet(channelKey.Contract, request.Channel, out sub)) { // No subscription found, return an empty one return(new PresenceResponse(request.Channel, 0, null)); } // Current list var presenceList = sub.Presence .Where(p => !p.Deleted) .Take(1000) .Select(p => p.Value.AsInfo()) .ToList(); // If we requested a presence, prepare a response return(new PresenceResponse(request.Channel, sub.Occupancy, presenceList)); }
/// <summary> /// Occurs when the remote client attempts to unsubscribe from a hub. /// </summary> /// <param name="client">The remote client.</param> /// <param name="channel">The full channel string.</param> public static EmitterEventCode Process(IClient client, string channel) { try { // Parse the channel EmitterChannel info; if (!EmitterChannel.TryParse(channel, true, out info)) { return(EmitterEventCode.BadRequest); } // Attempt to parse the key SecurityKey key; if (!SecurityKey.TryParse(info.Key, out key)) { return(EmitterEventCode.BadRequest); } // Has the key expired? if (key.IsExpired) { return(EmitterEventCode.Unauthorized); } // Have we already unsubscribed? //if (client[channel] == null) // return EmitterEventCode.Success; // Attempt to fetch the contract using the key. Underneath, it's cached. var contract = Services.Contract.GetByKey(key.Contract) as EmitterContract; if (contract == null) { return(EmitterEventCode.NotFound); } // Check if the payment state is valid if (contract.Status == EmitterContractStatus.Refused) { return(EmitterEventCode.PaymentRequired); } // Validate the contract if (!contract.Validate(ref key)) { return(EmitterEventCode.Unauthorized); } // Check if the key has the permission to read here if (!key.HasPermission(SecurityAccess.Read)) { return(EmitterEventCode.Unauthorized); } // Check if the key has the permission for the required channel if (info.Target != key.Target) { return(EmitterEventCode.Unauthorized); } // Get the subscription id from the storage if (!Dispatcher.Unsubscribe(client, key.Contract, info.Channel, SubscriptionInterest.Messages)) { return(EmitterEventCode.Unauthorized); } // Successfully unsubscribed return(EmitterEventCode.Success); } catch (NotImplementedException) { // We've got a not implemented exception return(EmitterEventCode.NotImplemented); } catch (Exception ex) { // We need to log it Service.Logger.Log(ex); // We've got a an internal error return(EmitterEventCode.ServerError); } }