Exemplo n.º 1
0
        internal static string GenerateSignalNowKey(string userName, string companyOrTenant, string groupOrTeam, string signalNowKey)
        {
            string nameHash1 = SignalRGroupUtils.GetNameHash(
                SignalRGroupUtils.GetUserGroupId(_hashSecuritySeed, companyOrTenant, groupOrTeam, userName),
                signalNowKey);
            string nameHash2 = SignalRGroupUtils.GetNameHash(nameHash1, signalNowKey);

            return(nameHash1 + nameHash2);
        }
Exemplo n.º 2
0
        public string GenerateAccessToken(string audience, string userId, TimeSpan?lifetime = null)
        {
            IEnumerable <Claim> claims = null;

            if (userId != null)
            {
                claims = new[]
                {
                    new Claim(ClaimTypes.GivenName, userId),
                    new Claim(ClaimTypes.NameIdentifier, SignalRGroupUtils.GetNameHash(userId, AccessKey))
                };
            }

            return(GenerateAccessTokenInternal(audience, claims, lifetime ?? TimeSpan.FromHours(1)));
        }
Exemplo n.º 3
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, Route = null)] HttpRequest req,
            ILogger log)
        {
            string errorMessage = string.Empty;

            var deviceId         = req.Headers["deviceid"].ToString().ToLowerInvariant();        // Device id, either a MAC address or a random string per session
            var userName         = req.Headers["username"].ToString().ToLowerInvariant();        // Username as registered in choosen authernication service (e.g. AAD userPrincipalName, GitHub username)
            var team             = req.Headers["teamname"].ToString().ToLowerInvariant();        // Teamname - id or name of the target group or team. Must be group id for AAD, team id for GitHub
            var company          = req.Headers["companyname"].ToString().ToLowerInvariant();     // Company name or tenant id. Must be tenant id for AAD, company id for GitHub
            var authServiceToken = req.Headers["authservicetoken"].ToString();                   // Authentication token received from authentication service. Bearer token for AAD or GitHub.
            var authService      = req.Headers["authservicename"].ToString().ToLowerInvariant(); // Authentication sercvice name. "graph.microsoft.com" for AAD, "github.com" for GitHub

            var             provider   = GraphServiceFactory.GetGraphService(authService);
            GraphAuthStatus authStatus = GraphAuthStatus.Unauthorized;

            if (provider == null)
            {
                errorMessage = $"{authService} is not a valid graph service name.";
            }
            else
            {
                authStatus = await provider.IsGroupMember(userName, company, team, authServiceToken, log);
            }

            ObjectResult funcResult = null;
            string       userId     = string.Empty;

            if (authStatus == GraphAuthStatus.OK)
            {
                userId = SignalRGroupUtils.GetFullUserId(authService, company, team, userName, deviceId);
                userId = userId.ToLowerInvariant();

                ServiceUtils utils       = new ServiceUtils(ConfigUtils.GetSignalRConnection());
                var          hubName     = ConfigUtils.GetHubName();
                var          clientUrl   = $"{utils.Endpoint}/client/?hub={hubName}";
                var          clientToken = utils.GenerateAccessToken(clientUrl,
                                                                     userId,
                                                                     TimeSpan.FromMinutes(UInt64.Parse(ConfigUtils.GetAuthTokenLifetimeMinutes())));

                string serverTime = ((long)(DateTime.UtcNow - DateTime.UnixEpoch).TotalSeconds).ToString();
                string turnServersAuthorization = ConfigUtils.MakeTURNAuthToken(company);

                funcResult = new OkObjectResult(new string[] { userId, clientToken, clientUrl, serverTime, turnServersAuthorization });
            }
            else
            {
                if (string.IsNullOrEmpty(errorMessage))
                {
                    errorMessage = $"Graph verification failed. Reason: {authStatus}";
                }
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                log.LogError(errorMessage);
            }
            else
            {
                log.LogInformation($"Successfully negotiated for {userName} as {userId}");
            }

            return(string.IsNullOrEmpty(errorMessage)
                ? (ActionResult)funcResult
                : new BadRequestObjectResult(new { message = errorMessage }));
        }
Exemplo n.º 4
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            var authToken   = req.Headers["authtoken"].ToString();                 // SignalNow authentication token received from GetMessageToken
            var sendTo      = req.Headers["sendto"].ToString().ToLowerInvariant(); // SignalNow username or group name
            var userOrGroup = req.Headers["userorgroup"].ToString();               // Message's recipient - "user" or "group"
            var messageType = req.Headers["messagetype"].ToString();               // Message type (any string, defined by the signaling protocol)

            string payload = string.Empty;

            // Message payload may be passed in "payload" header or, if there is no such header, via request body
            if (req.Headers.ContainsKey("payload"))
            {
                payload = req.Headers["payload"].ToString();
            }
            else
            {
                // If message pack was used to create playload, there should be "withmessagepack" header.
                // If its value is "lz", then payload is compressed with MessagePack.LZ4MessagePackSerializer.
                // We don't use req.ContentType because of ambiguity between application/x-msgpack and application/msgpack
                // https://github.com/msgpack/msgpack/issues/194 (plus we also need to differentiate between uncompressed and compressed)
                if (req.Headers.ContainsKey("withmessagepack"))
                {
                    var  mpValue = req.Headers["withmessagepack"].ToString();
                    bool useLZ   = string.Equals("lz", mpValue, StringComparison.InvariantCultureIgnoreCase);

                    int capacity = 0;
                    if (req.Body.Length >= 0 && req.Body.Length <= int.MaxValue)
                    {
                        capacity = (int)req.Body.Length;
                    }

                    using (var ms = new MemoryStream(capacity))
                    {
                        await req.Body.CopyToAsync(ms);

                        byte[] messageData = ms.ToArray();

                        payload = useLZ ?
                                  MessagePack.LZ4MessagePackSerializer.ToJson(messageData)
                                        : MessagePack.MessagePackSerializer.ToJson(messageData);
                    }
                }
                else
                {
                    using (var sr = new StreamReader(req.Body))
                    {
                        payload = await sr.ReadToEndAsync();
                    }
                }
            }

            string       userIdHash, userId;
            ServiceUtils utils = new ServiceUtils(ConfigUtils.GetSignalRConnection());

            if (!utils.ParseAndValidateToken(authToken, out userIdHash, out userId, null))
            {
                log.LogError($"Invalid authentication token ({authToken})");
                return(new UnauthorizedResult());
            }

            if (!SignalRGroupUtils.CanSendMessage(userId, sendTo))
            {
                log.LogError($"{userId} is not allowed to send messages to {sendTo}");
                return(new BadRequestObjectResult(new { message = $"{userId} is not allowed to send messages to {sendTo}" }));
            }

            var hubName = ConfigUtils.GetHubName();

            var sendToHash = SignalRGroupUtils.GetNameHash(sendTo, utils.AccessKey);

            string url = string.Equals(userOrGroup, "group", StringComparison.InvariantCultureIgnoreCase) ?
                         $"{utils.Endpoint}/api/v1/hubs/{hubName}/groups/{sendToHash}" :
                         $"{utils.Endpoint}/api/v1/hubs/{hubName}/users/{sendToHash}";
            string sendToken = utils.GenerateAccessToken(url, userId, TimeSpan.FromMinutes(1));

            PayloadMessage message = new PayloadMessage()
            {
                Target    = "SIGNAL",
                Arguments = new[]
                {
                    userId,
                    messageType,
                    payload
                }
            };

            bool           msgSent    = false;
            string         errMessage = string.Empty;
            HttpStatusCode httpStatus = HttpStatusCode.OK;
            string         errPhrase  = string.Empty;
            string         errContent = string.Empty;

            for (int i = 0; i < maxTries; i++)
            {
                try
                {
                    using (var request = new HttpRequestMessage(HttpMethod.Post, url))
                    {
                        request.Version = System.Net.HttpVersion.Version20;
                        request.Headers.Authorization =
                            new AuthenticationHeaderValue("Bearer", sendToken);
                        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                        request.Content = new StringContent(JsonConvert.SerializeObject(message, Formatting.None), Encoding.UTF8, "application/json");

                        using (var requestResult = await SignalRHttpClient.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
                        {
                            if (requestResult.IsSuccessStatusCode)
                            {
                                msgSent = true;
                                log.LogInformation($"{userId} sent a message to {sendTo}");
                            }
                            else
                            {
                                errPhrase = requestResult.ReasonPhrase;
                                if (requestResult.Content != null)
                                {
                                    errContent = await requestResult.Content.ReadAsStringAsync();
                                }
                                log.LogError($"Cannot send signal. Error {requestResult.StatusCode}: {requestResult.ReasonPhrase}");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    errMessage = ex.Message;
                    errPhrase  = ex.GetType().Name;
                    errContent = ex.Message;
                    log.LogError($"Exception when sending a SignalR request: {errMessage}");
                }

                if (msgSent)
                {
                    break;
                }
            }

            if (msgSent)
            {
                return((ActionResult) new OkResult());
            }
            else
            {
                return(new BadRequestObjectResult(
                           new { message = $"Cannot send signal. Error {httpStatus}: {errPhrase} ({errContent})" }
                           ));
            }
        }
Exemplo n.º 5
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            var          authToken  = req.Headers["authtoken"].ToString();
            ServiceUtils utils      = new ServiceUtils(ConfigUtils.GetSignalRConnection());
            string       userIdHash = string.Empty;
            string       userId     = string.Empty;

            if (!utils.ParseAndValidateToken(authToken, out userIdHash, out userId, null))
            {
                log.LogError($"GetMessageToken: Invalid authentication token ({authToken})");
                return(new BadRequestObjectResult("Invalid authentication token"));
            }

            TimeSpan messageTokenLifetime = TimeSpan.FromMinutes(UInt64.Parse(ConfigUtils.GetAuthTokenLifetimeMinutes()));
            var      messageToken         = utils.GenerateAccessToken(utils.Endpoint,
                                                                      userId,
                                                                      messageTokenLifetime);

            string errorMessage = string.Empty;
            string userName;
            string company;
            string team;
            string authService;
            string deviceId;

            SignalRGroupUtils.ParseUserId(userId, out userName, out deviceId, out company, out team, out authService);

            var hubName = ConfigUtils.GetHubName();
            var groups  = SignalRGroupUtils.GetUserGroups(authService, company, team, userName, deviceId);

            foreach (var group in groups)
            {
                var groupHash = SignalRGroupUtils.GetNameHash(group, utils.AccessKey);

                var url          = $"{utils.Endpoint}/api/v1/hubs/{hubName}/groups/{groupHash}/users/{userIdHash}";
                var requestToken = utils.GenerateAccessToken(url, userId, TimeSpan.FromMinutes(1));

                using (var request = new HttpRequestMessage(HttpMethod.Put, url))
                {
                    request.Version = System.Net.HttpVersion.Version20;
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", requestToken);
                    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                    using (var httpResult = await SignalRHttpClient.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
                    {
                        if (!httpResult.IsSuccessStatusCode)
                        {
                            var body = await httpResult.Content.ReadAsStringAsync();

                            errorMessage = $"Cannot add user {userId} ({userIdHash}) to group {group} ({groupHash}). Code: {httpResult.StatusCode}, Message: {httpResult.ReasonPhrase}, Body: {body}";
                            log.LogError(errorMessage);
                            break;
                        }
                    }
                }
            }

            return(string.IsNullOrEmpty(errorMessage)
                ? (ActionResult) new OkObjectResult(messageToken)
                : new BadRequestObjectResult(new { message = errorMessage }));
        }