dex-update-passwd: Initial commit
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
e23377329a
commit
9c50bc37d9
4 changed files with 229 additions and 469 deletions
BIN
remote/dex-update-passwd/dex-update-passwd
Executable file
BIN
remote/dex-update-passwd/dex-update-passwd
Executable file
Binary file not shown.
111
remote/dex-update-passwd/main.go
Normal file
111
remote/dex-update-passwd/main.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/dexidp/dex/api/v2"
|
||||
"github.com/dexidp/dex/storage"
|
||||
"github.com/ghodss/yaml"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Keep Config struct in sync with dex one.
|
||||
|
||||
// password is storage.Password
|
||||
// https://github.com/dexidp/dex/blob/master/storage/storage.go#L336
|
||||
type password storage.Password
|
||||
|
||||
func (p *password) UnmarshalJSON(b []byte) error {
|
||||
var data struct {
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
UserID string `json:"userID"`
|
||||
Hash string `json:"hash"`
|
||||
HashFromEnv string `json:"hashFromEnv"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
*p = password(storage.Password{
|
||||
Email: data.Email,
|
||||
Username: data.Username,
|
||||
UserID: data.UserID,
|
||||
})
|
||||
if len(data.Hash) == 0 && len(data.HashFromEnv) > 0 {
|
||||
data.Hash = os.Getenv(data.HashFromEnv)
|
||||
}
|
||||
if len(data.Hash) == 0 {
|
||||
return fmt.Errorf("no password hash provided")
|
||||
}
|
||||
|
||||
// If this value is a valid bcrypt, use it.
|
||||
_, bcryptErr := bcrypt.Cost([]byte(data.Hash))
|
||||
if bcryptErr == nil {
|
||||
p.Hash = []byte(data.Hash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// For backwards compatibility try to base64 decode this value.
|
||||
hashBytes, err := base64.StdEncoding.DecodeString(data.Hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("malformed bcrypt hash: %v", bcryptErr)
|
||||
}
|
||||
if _, err := bcrypt.Cost(hashBytes); err != nil {
|
||||
return fmt.Errorf("malformed bcrypt hash: %v", err)
|
||||
}
|
||||
p.Hash = hashBytes
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://github.com/dexidp/dex/blob/master/cmd/dex/config.go#L25
|
||||
type Config struct {
|
||||
StaticPasswords []password `json:"staticPasswords"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if flag.NArg() != 1 {
|
||||
log.Fatal("Need one argument: the new config file")
|
||||
}
|
||||
|
||||
// Load dex config file
|
||||
configData, err := os.ReadFile(flag.Args()[0])
|
||||
if err != nil {
|
||||
log.Fatalf("failed to read config file %s: %v", flag.Args()[0], err)
|
||||
}
|
||||
|
||||
var c Config
|
||||
if err := yaml.Unmarshal(configData, &c); err != nil {
|
||||
log.Fatalf("error parse config file %s: %v", flag.Args()[0], err)
|
||||
}
|
||||
|
||||
if len(c.StaticPasswords) == 0 {
|
||||
log.Fatalf("no password in config file. Aborting")
|
||||
}
|
||||
|
||||
// Connect to dex through GRPC API
|
||||
conn, err := grpc.Dial("127.0.0.1:5557", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
log.Fatalf("failed creating dex client: %v ", err)
|
||||
}
|
||||
client := api.NewDexClient(conn)
|
||||
|
||||
for _, passwd := range c.StaticPasswords {
|
||||
req := &api.UpdatePasswordReq{
|
||||
Email: passwd.Email,
|
||||
NewHash: passwd.Hash,
|
||||
}
|
||||
|
||||
if _, err := client.UpdatePassword(context.TODO(), req); err != nil {
|
||||
log.Printf("failed update password for %q: %v", passwd.Email, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in a new issue