예제 #1
0
        /// <summary>
        /// Generates a JWT with RSA512 using a private key loaded from the environment.
        /// </summary>
        /// <remarks>
        /// A JWT is compromised of 3 dot-separated, base64 strings.
        /// Jose_Header.JWT_Payload.JWT_Signature
        /// </remarks>
        /// <param name="payload">The data to be encrypted.</param>
        /// <returns>The JWT.</returns>
        public string GenerateJWT(Dictionary <string, string> payload)
        {
            // TODO CHECK PUBLIC KEY AND PRIVATE KEY, CHECK THEIR LENGTHS AND IF THEY INCLUDE ----BEGIN... ---END... ETC

            // Make sure we have the proper parameters inside the dictionary
            if (!payload.ContainsKey(Constants.UserTypeKey) || !payload.ContainsKey(Constants.IdKey))
            {
                throw new ArgumentException("UserType or ID was not provided.");
            }

            // Create the header and convert it to a Base64 string
            Dictionary <string, string> joseHeader = new Dictionary <string, string> {
                { Constants.MediaType, Constants.MediaJWT },  // Media type
                { Constants.SigningAlgKey, Constants.SIGNING_ALGORITHM }  // Signing algorithm type
            };

            // If the expiration date wasn't already specified, then create one
            if (!payload.ContainsKey(Constants.EXPIRATION_FIELD))
            {
                // Add a 20 min expiration
                payload.Add(Constants.EXPIRATION_FIELD, TimeUtilityService.GetEpochFromNow().ToString());
            }

            // Base64 encode the header and payload
            string encodedHeader  = StringUtilityService.DictionaryToString(joseHeader).ToBase64URL();
            string encodedPayload = StringUtilityService.DictionaryToString(payload).ToBase64URL();

            // The signature will be the hash of the header and payload
            string stringToSign = encodedHeader + '.' + encodedPayload;

            // Create the signature
            string signature = GetPKCSSignature(stringToSign).ToBase64URL();

            return(string.Format("{0}.{1}.{2}", encodedHeader, encodedPayload, signature));
        }
예제 #2
0
        /// <summary>
        /// Refreshes a token to be active for 20 more minutes.
        /// </summary>
        /// <param name="jwt">The token that needs to be refreshed.</param>
        /// <returns>A new token that has been refreshed and active for 20 more minutes.</returns>
        public string RefreshJWT(string jwt, int minutes = Constants.TOKEN_EXPIRATION_MIN)
        {
            Dictionary <string, string> payload = _authorizationService.DecryptJWT(jwt);

            // Refresh the token for an additional 20 minutes
            payload[Constants.EXPIRATION_FIELD] = TimeUtilityService.GetEpochFromNow(minutes).ToString();

            return(_authorizationService.GenerateJWT(payload));
        }
예제 #3
0
        /// <summary>
        /// Asynchronously send an email code to <paramref name="emailAddress"/> of the user
        /// indicated by the <paramref name="username"/>.
        /// </summary>
        /// <param name="username">The username of the user to send the email to (string)</param>
        /// <param name="emailAddress">The email address to send the email to (string)</param>
        /// <returns>Task (bool) whether the function executed without exception</returns>
        public async Task <bool> SendEmailVerificationAsync(string username, string emailAddress)
        {
            var message     = new MimeMessage();
            var bodyBuilder = new BodyBuilder();

            // From the system email address to the emailAddress.
            message.From.Add(new MailboxAddress(Constants.SystemEmailAddress));
            message.To.Add(new MailboxAddress($"{emailAddress}"));

            message.Subject = Constants.EmailVerificationSubject;

            // Generate a code the length of what's defined in the constant file.
            Random generator = new Random();
            string emailCode = generator.Next((int)Math.Pow(10, Constants.EmailCodeLength - 1),
                                              (int)Math.Pow(10, Constants.EmailCodeLength)).ToString();

            // Timestamp the email code was generated at.
            long emailCodeTimestamp = TimeUtilityService.CurrentUnixTime();

            // Store the code and timestamp in the data store.
            await _userManagementService.StoreEmailCodeAsync(username, emailCode.ToString(), emailCodeTimestamp).ConfigureAwait(false);

            // Html body for the email code.
            bodyBuilder.HtmlBody = @"<td valign=""top"" align=""center"" bgcolor=""#0d1121"" style=""padding:35px 70px 30px;"" class=""em_padd""><table align=""center"" width=""100%"" border=""0"" cellspacing=""0"" cellpadding=""0"">" +
                                   @"<tr>" +
                                   @"</tr>" +
                                   @"<tr>" +
                                   @"<td height = ""15"" style = ""font-size:0px; line-height:0px; height:15px;"" > &nbsp;</td>" +
                                   @"</tr>" +
                                   @"<tr>" +
                                   $@"<td align = ""center"" valign = ""top"" style = ""font-family:'Open Sans', Arial, sans-serif; font-size:18px; line-height:22px; color:#fbeb59; letter-spacing:2px; padding-bottom:12px;"">YOUR EMAIL VERIFICATION CODE IS: {emailCode}</td>" +
                                   @"</tr>" +
                                   @"<tr>";

            message.Body = bodyBuilder.ToMessageBody();

            // Create the SMTP client with the default certificate validation callback to prevent man in the middle attacks.
            //var client = new SmtpClient
            //{
            //    ServerCertificateValidationCallback = (s, c, h, e) => MailService.DefaultServerCertificateValidationCallback(s, c, h, e)
            //};
            var client = new SmtpClient();

            // Connect over google SMTP, provide credentials, and asynchronously send and disconnect the client.
            await client.ConnectAsync(Constants.GoogleSMTP, Constants.GoogleSMTPPort, SecureSocketOptions.SslOnConnect).ConfigureAwait(false);

            await client.AuthenticateAsync(Constants.SystemEmailAddress, Constants.SystemEmailPassword).ConfigureAwait(false);

            await client.SendAsync(message).ConfigureAwait(false);

            await client.DisconnectAsync(true).ConfigureAwait(false);

            client.Dispose();

            return(true);
        }
예제 #4
0
        /// <summary>
        /// Submits a ticket to the system
        /// </summary>
        /// <param name="category">The category of the ticket</param>
        /// <param name="description">The description inside the ticket</param>
        /// <returns>Whether the ticket was saved or not</returns>
        public async Task <bool> SubmitTicketAsync(Constants.TicketCategories category, string description)
        {
            TicketRecord ticketRecord = new TicketRecord(TimeUtilityService.CurrentUnixTime(),
                                                         category.ToString(),
                                                         Constants.TicketStatuses.Unresolved.ToString(),
                                                         Constants.TicketFlagColors.None.ToString(),
                                                         description);
            await ticketDAO.CreateAsync(ticketRecord);

            return(true);
        }
예제 #5
0
        /// <summary>
        /// Increment the registration failures of the anonymous user defined by the <paramref name="ipAddress"/>.
        /// </summary>
        /// <param name="ipAddress">The ip address to increment the registration failures of (string)</param>
        /// <param name="maxTimeBeforeFailureReset">The time before their failures reset (TimeSpan)</param>
        /// <param name="maxNumberOfTries">The max number of registration tries before they get locked (int)</param>
        /// <returns>Task (bool) whether the funciton executed without exception</returns>
        public async Task <bool> IncrementRegistrationFailuresAsync(string ipAddress, TimeSpan maxTimeBeforeFailureReset, int maxNumberOfTries)
        {
            IPAddressObject ip = await GetIPAddressInfoAsync(ipAddress).ConfigureAwait(false);

            IPAddressRecord record;

            // Need to check if the maxtime + lastTime is less than now.
            // if it is then reset the failure
            long lastRegFailTimestamp = ip.LastRegFailTimestamp;
            long maxSeconds           = TimeUtilityService.TimespanToSeconds(maxTimeBeforeFailureReset);
            long currentUnix          = TimeUtilityService.CurrentUnixTime();

            bool reset = false;

            // If the time has passed their max time before reset, reset their failures. Don't reset
            // if they have no last registration fail timestamp.
            if (lastRegFailTimestamp + maxSeconds < currentUnix && lastRegFailTimestamp != Constants.NoValueLong)
            {
                reset  = true;
                record = new IPAddressRecord(ipAddress, registrationFailures: 0);

                await UpdateIPAsync(record).ConfigureAwait(false);
            }

            // Increment the user's login Failure count.
            int updatedRegistrationFailures = reset ? 1 : ip.RegistrationFailures + 1;

            // Lock the ip if they have reached the max number of tries.
            // Update the last reg fail time.
            if (updatedRegistrationFailures >= maxNumberOfTries)
            {
                record = new IPAddressRecord(ipAddress, timestampLocked: currentUnix, registrationFailures: updatedRegistrationFailures, lastRegFailTimestamp: currentUnix);

                // Asynchronously notify the system admin if an ip address was locked during registration.
                await SystemUtilityService.NotifySystemAdminAsync($"{ipAddress} was locked at {currentUnix}", Constants.SystemAdminEmailAddress).ConfigureAwait(false);
            }
            else
            {
                record = new IPAddressRecord(ipAddress, registrationFailures: updatedRegistrationFailures, lastRegFailTimestamp: currentUnix);
            }

            return(await UpdateIPAsync(record).ConfigureAwait(false));
        }
예제 #6
0
        /// <summary>
        /// Asynchronously create a user in the data store.
        /// </summary>
        /// <param name="isTemp">Used to specify whether the user is temporary.</param>
        /// <param name="record">The unmasked record conveying the data to create</param>
        /// <param name="adminName">The username of the admin performing this operation (default = system)</param>
        /// <param name="adminIp">The ip address of the admin performing this operation (default = localhost)</param>
        /// <returns>Task (bool) whether the function completed without exception</returns>
        public async Task <bool> CreateUserAsync(bool isTemp, UserRecord record, string adminName = Constants.SystemIdentifier, string adminIp = Constants.LocalHost)
        {
            // Check that the user of function is an admin and throw an exception if they are not.
            if (!adminName.Equals(Constants.SystemIdentifier))
            {
                UserObject admin = await GetUserInfoAsync(adminName).ConfigureAwait(false);

                if (admin.UserType != Constants.AdminUserType)
                {
                    throw new ArgumentException(Constants.MustBeAdmin);
                }
            }

            // Record must be unmasked.
            if (record.IsMasked())
            {
                throw new ArgumentException(Constants.CreateUserRecordMasked);
            }

            // Check for user existence and throw an exception if the user already exists.
            if (await CheckUserExistenceAsync(record.GetData()[Constants.UserDAOusernameColumn] as string).ConfigureAwait(false))
            {
                throw new ArgumentException(Constants.UsernameExistsLogMessage);
            }

            // If the user being created is temporary, update the timestamp to be the current unix time, otherwise
            // the timestamp has no value.
            long tempTimestamp = isTemp ? TimeUtilityService.CurrentUnixTime() : Constants.NoValueLong;

            record.GetData()[Constants.UserDAOtempTimestampColumn] = tempTimestamp;

            // Mask all the sensitive information.
            UserRecord resultRecord = await _maskingService.MaskAsync(record, true).ConfigureAwait(false) as UserRecord;

            // Insert the masked user into the datastore.
            await _userDAO.CreateAsync(resultRecord).ConfigureAwait(false);

            // Log the action.
            await LogAsync(DateTime.UtcNow.ToString(Constants.LoggingFormatString), Constants.SingleUserCreateOperation,
                           adminName, adminIp).ConfigureAwait(false);

            return(true);
        }
예제 #7
0
        /// <summary>
        /// Asynchronously increment the login failures of a user and disables that user if he has
        /// reached the <paramref name="maxNumberOfTries"/> parameter and his last login failure time
        /// hs not exceeded the <paramref name="maxTimeBeforeFailureReset"/> parameter.
        /// </summary>
        /// <param name="username">Username of the user to increment</param>
        /// <param name="maxTimeBeforeFailureReset">TimeSpan object to represent how long the before the login failure resets.</param>
        /// <param name="maxNumberOfTries">The max number of tries a user can login before he is disabled.</param>
        /// <returns>Returns true if the operation is successfull and false if it failed.</returns>
        public async Task <bool> IncrementLoginFailuresAsync(string username, TimeSpan maxTimeBeforeFailureReset, int maxNumberOfTries)
        {
            UserObject user = await GetUserInfoAsync(username).ConfigureAwait(false);

            UserRecord record;

            // Need to check if the maxtime + lastTime is less than now.
            // if it is then reset the failure
            long lastLoginFailTimestamp = user.LastLoginFailTimestamp;
            long maxSeconds             = TimeUtilityService.TimespanToSeconds(maxTimeBeforeFailureReset);
            long currentUnix            = TimeUtilityService.CurrentUnixTime();

            bool reset = false;

            // If the time has passed their max time before reset, reset their failures. Don't reset if
            // they have no last login fail timestamp.
            if (lastLoginFailTimestamp + maxSeconds < currentUnix && lastLoginFailTimestamp != Constants.NoValueLong)
            {
                reset  = true;
                record = new UserRecord(username, loginFailures: 0);

                await UpdateUserAsync(record).ConfigureAwait(false);
            }

            // Increment the user's login failure count.
            int updatedLoginFailures = reset ? 1 : user.LogInFailures + 1;

            // Disable the user if they have reached the max number of tries.
            // Update the last login fail time.
            if (updatedLoginFailures >= maxNumberOfTries)
            {
                record = new UserRecord(username, loginFailures: updatedLoginFailures, disabled: Constants.DisabledStatus, lastLoginFailTimestamp: currentUnix);
            }
            else
            {
                record = new UserRecord(username, loginFailures: updatedLoginFailures, lastLoginFailTimestamp: currentUnix);
            }

            await UpdateUserAsync(record).ConfigureAwait(false);

            return(true);
        }
예제 #8
0
        /// <summary>
        /// Checks whether the current token is expired or not.
        /// </summary>
        /// <param name="jwt">The token to check.</param>
        /// <returns>Whether the current token is past it's 20 minute lifetime.</returns>
        public bool TokenIsExpired(string jwt)
        {
            Dictionary <string, string> payload = _authorizationService.DecryptJWT(jwt);

            // Check if the expiration key exists first
            if (!payload.ContainsKey(Constants.EXPIRATION_FIELD))
            {
                throw new ArgumentException("Expiration time is not specified!");
            }

            long expTime;
            bool isNumeric = long.TryParse(payload[Constants.EXPIRATION_FIELD], out expTime);

            // Make sure we are dealing with a number first
            if (!isNumeric)
            {
                throw new ArgumentException("Expiration time is not a number!");
            }

            return(TimeUtilityService.CurrentUnixTime() > expTime);
        }