-
-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reset a user password using CLI (#226)
- Loading branch information
Showing
8 changed files
with
140 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Reset a user password | ||
|
||
To reset a user password, run the following command using the Opengist binary: | ||
|
||
```bash | ||
./opengist admin reset-password <username> <new-password> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"github.com/thomiceli/opengist/internal/db" | ||
"github.com/thomiceli/opengist/internal/utils" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
var CmdAdmin = cli.Command{ | ||
Name: "admin", | ||
Usage: "Admin commands", | ||
Subcommands: []*cli.Command{ | ||
&CmdAdminResetPassword, | ||
}, | ||
} | ||
|
||
var CmdAdminResetPassword = cli.Command{ | ||
Name: "reset-password", | ||
Usage: "Reset the password for a given user", | ||
ArgsUsage: "[username] [password]", | ||
Action: func(ctx *cli.Context) error { | ||
initialize(ctx) | ||
if ctx.NArg() < 2 { | ||
return fmt.Errorf("username and password are required") | ||
} | ||
username := ctx.Args().Get(0) | ||
plainPassword := ctx.Args().Get(1) | ||
|
||
user, err := db.GetUserByUsername(username) | ||
if err != nil { | ||
fmt.Printf("Cannot get user %s: %s\n", username, err) | ||
return err | ||
} | ||
password, err := utils.Argon2id.Hash(plainPassword) | ||
if err != nil { | ||
fmt.Printf("Cannot hash password for user %s: %s\n", username, err) | ||
return err | ||
} | ||
user.Password = password | ||
|
||
if err = user.Update(); err != nil { | ||
fmt.Printf("Cannot update password for user %s: %s\n", username, err) | ||
return err | ||
} | ||
|
||
fmt.Printf("Password for user %s has been reset.\n", username) | ||
return nil | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package utils | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/subtle" | ||
"encoding/base64" | ||
"errors" | ||
"fmt" | ||
"golang.org/x/crypto/argon2" | ||
"strings" | ||
) | ||
|
||
type Argon2ID struct { | ||
format string | ||
version int | ||
time uint32 | ||
memory uint32 | ||
keyLen uint32 | ||
saltLen uint32 | ||
threads uint8 | ||
} | ||
|
||
var Argon2id = Argon2ID{ | ||
format: "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", | ||
version: argon2.Version, | ||
time: 1, | ||
memory: 64 * 1024, | ||
keyLen: 32, | ||
saltLen: 16, | ||
threads: 4, | ||
} | ||
|
||
func (a Argon2ID) Hash(plain string) (string, error) { | ||
salt := make([]byte, a.saltLen) | ||
if _, err := rand.Read(salt); err != nil { | ||
return "", err | ||
} | ||
|
||
hash := argon2.IDKey([]byte(plain), salt, a.time, a.memory, a.threads, a.keyLen) | ||
|
||
return fmt.Sprintf(a.format, a.version, a.memory, a.time, a.threads, | ||
base64.RawStdEncoding.EncodeToString(salt), | ||
base64.RawStdEncoding.EncodeToString(hash), | ||
), nil | ||
} | ||
|
||
func (a Argon2ID) Verify(plain, hash string) (bool, error) { | ||
if hash == "" { | ||
return false, nil | ||
} | ||
|
||
hashParts := strings.Split(hash, "$") | ||
|
||
if len(hashParts) != 6 { | ||
return false, errors.New("invalid hash") | ||
} | ||
|
||
_, err := fmt.Sscanf(hashParts[3], "m=%d,t=%d,p=%d", &a.memory, &a.time, &a.threads) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
salt, err := base64.RawStdEncoding.DecodeString(hashParts[4]) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
decodedHash, err := base64.RawStdEncoding.DecodeString(hashParts[5]) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
hashToCompare := argon2.IDKey([]byte(plain), salt, a.time, a.memory, a.threads, uint32(len(decodedHash))) | ||
|
||
return subtle.ConstantTimeCompare(decodedHash, hashToCompare) == 1, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters