Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.

If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!

Question about Salt_Bytes



  • Re: Reset ProGet Admin Password via API

    I've got code using the Native API which creates a user account and works wonders. But as I'm doing this programmatically I need to also generate a random password for the account, and set it. Seems pretty straightforward to do....but I'm unsure about what to do with the Salt_Bytes parameter available on the API. Any pointers would be outstanding.



  • Here's what I'm using currently, but it's complaining about Salt_Bytes. I'm fairly certain this is a PowerShell problem, but more eyes on it never hurt.

    function Set-ProGetUserPassword {
        [Cmdletbinding()]
        Param(
            [Parameter(Mandatory)]
            [String]
            $Username,
    
            [Parameter(Mandatory)]
            [SecureString]
            $OldPassword,
    
            [Parameter(Mandatory)]
            [SecureString]
            $NewPassword
        )
    
        begin {
            $confirm = Read-Host "Please re-enter your new password" -AsSecureString
    
            $identical = Compare-SecureString -SecureString1 $NewPassword -SecureString2 $confirm
    
            if(-not $identical){
                Write-Error 'Passwords do not match!'
                break
            }
        }
    
        end {
    
            $unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($NewPassword)
            $plainTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($unmanagedString)
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($unmanagedString) # Clean up
            $iterations = 10000
            $saltLength = 10
            $hashLength = 20
    
            $rfc2898 = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($plainTextPassword, $saltLength, $iterations)
            $passwordBytes = $rfc2898.GetBytes($hashLength)
            $saltBytes = $rfc2898.Salt
    
            $base64Password = [System.Convert]::ToBase64String($passwordBytes)
    
            $body = @{
                User_Name = $UserName
                Password_Bytes = $base64Password
                Salt_Bytes = $saltBytes
            }
    
            Write-Verbose ($body | ConvertTo-Json)
    
            $Params = @{
                Slug = '/api/json/Users_SetPassword'
                Method = 'POST'
                Body = $body
            }
    
            Invoke-ProGet @Params
        }
    }
    

  • inedo-engineer

    Hi @steviecoaster ,

    I'm not really a PowerShell guru, but I think you'll want to do ...

        $base64Salt= [System.Convert]::ToBase64String($saltBytes)
    

    ... and then pass that in.

    Hope that does it!

    Alana



  • In my playing around yesterday, I did try to convert that to base64 and it still produced the same error. I'm very perplexed! For my use case I really need this functionality so I'm hopeful I can find a solution.



  • Sweet mother Mary, I got it! It was the content type. I was using application/json and it wanted application/x-ww-form-urlencoded

    This code works properly to set the password for a user:

    function Set-ProGetUserPassword {
        [Cmdletbinding()]
        Param(
            [Parameter(Mandatory)]
            [String]
            $Username,
    
            [Parameter(Mandatory)]
            [SecureString]
            $OldPassword,
    
            [Parameter(Mandatory)]
            [SecureString]
            $NewPassword
        )
    
        begin {
            $confirm = Read-Host "Please re-enter your new password" -AsSecureString
    
            $identical = Compare-SecureString -SecureString1 $NewPassword -SecureString2 $confirm
    
            if (-not $identical) {
                Write-Error 'Passwords do not match!'
                break
            }
        }
    
        end {
    
            $unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($NewPassword)
            $plainTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($unmanagedString)
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($unmanagedString) # Clean up
            $iterations = 10000
            $saltLength = 10
            $hashLength = 20
    
            $rfc2898 = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($plainTextPassword, $saltLength, $iterations)
            $passwordBytes = $rfc2898.GetBytes($hashLength)
            $saltBytes = $rfc2898.Salt
    
            $base64Password = [System.Convert]::ToBase64String($passwordBytes)
            $base64Salt = [System.Convert]::ToBase64String($saltBytes)
    
            $body = @{
                User_Name = $Username
                Password_Bytes = $base64Password
                Salt_Bytes = $base64Salt
            }
    
            Write-Verbose $body
    
            $Params = @{
                Slug     = '/api/json/Users_SetPassword'
                Method   = 'POST'
                Body = $body
                ContentType = 'application/x-www-form-urlencoded'
            }
    
            Invoke-ProGet @Params
        }
    }
    


  • @steviecoaster said in Question about Salt_Bytes:

    Sweet mother Mary, I got it! It was the content type. I was using application/json and it wanted application/x-ww-form-urlencoded

    This code works properly to set the password for a user:

    function Set-ProGetUserPassword {
        [Cmdletbinding()]
        Param(
            [Parameter(Mandatory)]
            [String]
            $Username,
    
            [Parameter(Mandatory)]
            [SecureString]
            $OldPassword,
    
            [Parameter(Mandatory)]
            [SecureString]
            $NewPassword
        )
    
        begin {
            $confirm = Read-Host "Please re-enter your new password" -AsSecureString
    
            $identical = Compare-SecureString -SecureString1 $NewPassword -SecureString2 $confirm
    
            if (-not $identical) {
                Write-Error 'Passwords do not match!'
                break
            }
        }
    
        end {
    
            $unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($NewPassword)
            $plainTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($unmanagedString)
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($unmanagedString) # Clean up
            $iterations = 10000
            $saltLength = 10
            $hashLength = 20
    
            $rfc2898 = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($plainTextPassword, $saltLength, $iterations)
            $passwordBytes = $rfc2898.GetBytes($hashLength)
            $saltBytes = $rfc2898.Salt
    
            $base64Password = [System.Convert]::ToBase64String($passwordBytes)
            $base64Salt = [System.Convert]::ToBase64String($saltBytes)
    
            $body = @{
                User_Name = $Username
                Password_Bytes = $base64Password
                Salt_Bytes = $base64Salt
            }
    
            Write-Verbose $body
    
            $Params = @{
                Slug     = '/api/json/Users_SetPassword'
                Method   = 'POST'
                Body = $body
                ContentType = 'application/x-www-form-urlencoded'
            }
    
            Invoke-ProGet @Params
        }
    }
    

    Spoke to soon. This code functions and doesn't produce an error. However, the user cannot login with the credential set. More playing to do!


  • inedo-engineer

    Hi @steviecoaster ,

    The Native API can be a little finicky, especially since you can invoke with JSON, forum-encoded values, querystring, and I think even XML. But it sounds like you're on the right track.

    Let me share the C# code that ProGet uses to set the password:

        using (var rfc2898 = new Rfc2898DeriveBytes(password ?? string.Empty, 10, 10000, HashAlgorithmName.SHA1))
        {
            var bytes = rfc2898.GetBytes(20);
            DB.Users_SetPassword(userName, bytes, rfc2898.Salt);
        }
    

    ... it looks a little different than the code you're using, so hopefully that will help!

    -- Dean


Log in to reply
 

Inedo Website HomeSupport HomeCode of ConductForums GuideDocumentation