Create User Service to support password management (#411)

* create User Api

* update UsersPasswordRequest to UpdatePasswordRequest

* update UserResponse to UpdatePasswordResponse

* current password only needs to be entered once
This commit is contained in:
JazminGonzalez-Rivero 2018-07-16 05:34:35 -04:00 committed by Jesse Suen
parent 39b9f4d31a
commit 062b13e92a
11 changed files with 1103 additions and 13 deletions

6
Gopkg.lock generated
View file

@ -1022,8 +1022,12 @@
[[projects]]
name = "k8s.io/kubernetes"
packages = [
"pkg/apis/apps",
"pkg/apis/autoscaling",
"pkg/apis/batch",
"pkg/apis/core",
"pkg/apis/extensions",
"pkg/apis/networking",
"pkg/kubectl/scheme"
]
revision = "81753b10df112992bf51bbc2c2f85208aad78335"
@ -1032,6 +1036,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "839d26383e983bd1388439f67b8ba360e3479b14c4a731f48413fa6ed78f6485"
inputs-digest = "2eaad7537e4d4bc23be336a9e96c3e589cefccf6913125a37bf9b069bf02b5e9"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -30,6 +30,7 @@ func NewCommand() *cobra.Command {
command.AddCommand(NewRepoCommand(&clientOpts))
command.AddCommand(NewContextCommand(&clientOpts))
command.AddCommand(NewProjectCommand(&clientOpts))
command.AddCommand(NewUsersCommand(&clientOpts))
defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath()
errors.CheckError(err)

View file

@ -0,0 +1,77 @@
package commands
import (
"context"
"fmt"
"os"
"syscall"
"github.com/argoproj/argo-cd/errors"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/server/users"
"github.com/argoproj/argo-cd/util"
"github.com/argoproj/argo-cd/util/settings"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
)
func NewUsersCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "users",
Short: "Manage users",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
},
}
command.AddCommand(NewUsersChangePasswordCommand(clientOpts))
return command
}
func NewUsersChangePasswordCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
CurrentPassword string
NewPassword string
)
var command = &cobra.Command{
Use: "change-password USERNAME",
Short: "Change User Password",
Run: func(c *cobra.Command, args []string) {
if len(args) == 0 {
c.HelpFunc()(c, args)
os.Exit(1)
}
if CurrentPassword == "" {
fmt.Print("*** Enter your Current Password password ")
password, err := terminal.ReadPassword(syscall.Stdin)
errors.CheckError(err)
CurrentPassword = string(password)
fmt.Print("\n")
}
if NewPassword == "" {
NewPassword = settings.ReadAndConfirmPassword()
}
userName := args[0]
body := users.Body{
CurrentPassword: CurrentPassword,
NewPassword: NewPassword,
}
UpdatePasswordRequest := users.UpdatePasswordRequest{
Name: userName,
Body: &body,
}
conn, usrIf := argocdclient.NewClientOrDie(clientOpts).NewUsersClientOrDie()
defer util.Close(conn)
_, err := usrIf.UpdatePassword(context.Background(), &UpdatePasswordRequest)
errors.CheckError(err)
fmt.Printf("password for user %s updated to %s \n", userName, NewPassword)
},
}
command.Flags().StringVar(&CurrentPassword, "current-password", "", "current password you wish to change")
command.Flags().StringVar(&NewPassword, "new-password", "", "new password you want to update to")
return command
}

View file

@ -17,6 +17,7 @@ import (
"github.com/argoproj/argo-cd/server/repository"
"github.com/argoproj/argo-cd/server/session"
"github.com/argoproj/argo-cd/server/settings"
"github.com/argoproj/argo-cd/server/users"
"github.com/argoproj/argo-cd/server/version"
grpc_util "github.com/argoproj/argo-cd/util/grpc"
"github.com/argoproj/argo-cd/util/localconfig"
@ -51,6 +52,8 @@ type Client interface {
NewVersionClientOrDie() (*grpc.ClientConn, version.VersionServiceClient)
NewProjectClient() (*grpc.ClientConn, project.ProjectServiceClient, error)
NewProjectClientOrDie() (*grpc.ClientConn, project.ProjectServiceClient)
NewUsersClient() (*grpc.ClientConn, users.UsersServiceClient, error)
NewUsersClientOrDie() (*grpc.ClientConn, users.UsersServiceClient)
}
// ClientOptions hold address, security, and other settings for the API client.
@ -312,3 +315,20 @@ func (c *client) NewProjectClientOrDie() (*grpc.ClientConn, project.ProjectServi
}
return conn, projIf
}
func (c *client) NewUsersClient() (*grpc.ClientConn, users.UsersServiceClient, error) {
conn, err := c.NewConn()
if err != nil {
return nil, nil, err
}
usrIf := users.NewUsersServiceClient(conn)
return conn, usrIf, nil
}
func (c *client) NewUsersClientOrDie() (*grpc.ClientConn, users.UsersServiceClient) {
conn, usrIf, err := c.NewUsersClient()
if err != nil {
log.Fatalf("Failed to establish connection to %s: %v", c.ServerAddr, err)
}
return conn, usrIf
}

View file

@ -41,6 +41,7 @@ import (
"github.com/argoproj/argo-cd/server/repository"
"github.com/argoproj/argo-cd/server/session"
"github.com/argoproj/argo-cd/server/settings"
"github.com/argoproj/argo-cd/server/users"
"github.com/argoproj/argo-cd/server/version"
"github.com/argoproj/argo-cd/util"
"github.com/argoproj/argo-cd/util/db"
@ -323,6 +324,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
applicationService := application.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.RepoClientset, db, a.enf, projectLock)
projectService := project.NewServer(a.Namespace, a.AppClientset, a.enf, projectLock)
settingsService := settings.NewServer(a.settingsMgr)
usersService := users.NewServer(a.sessionMgr, a.settingsMgr)
version.RegisterVersionServiceServer(grpcS, &version.Server{})
cluster.RegisterClusterServiceServer(grpcS, clusterService)
application.RegisterApplicationServiceServer(grpcS, applicationService)
@ -330,7 +332,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
session.RegisterSessionServiceServer(grpcS, sessionService)
settings.RegisterSettingsServiceServer(grpcS, settingsService)
project.RegisterProjectServiceServer(grpcS, projectService)
users.RegisterUsersServiceServer(grpcS, usersService)
// Register reflection service on gRPC server.
reflection.Register(grpcS)
return grpcS

View file

@ -22,7 +22,7 @@
"ApplicationService"
],
"summary": "List returns list of applications",
"operationId": "ListMixin5",
"operationId": "ListMixin6",
"parameters": [
{
"type": "string",
@ -58,7 +58,7 @@
"ApplicationService"
],
"summary": "Create creates an application",
"operationId": "CreateMixin5",
"operationId": "CreateMixin6",
"parameters": [
{
"name": "body",
@ -85,7 +85,7 @@
"ApplicationService"
],
"summary": "Update updates an application",
"operationId": "UpdateMixin5",
"operationId": "UpdateMixin6",
"parameters": [
{
"type": "string",
@ -118,7 +118,7 @@
"ApplicationService"
],
"summary": "Get returns an application by name",
"operationId": "GetMixin5",
"operationId": "GetMixin6",
"parameters": [
{
"type": "string",
@ -155,7 +155,7 @@
"ApplicationService"
],
"summary": "Delete deletes an application",
"operationId": "DeleteMixin5",
"operationId": "DeleteMixin6",
"parameters": [
{
"type": "string",
@ -890,7 +890,7 @@
"SessionService"
],
"summary": "Create a new JWT for authentication and set a cookie if using HTTP.",
"operationId": "CreateMixin6",
"operationId": "CreateMixin7",
"parameters": [
{
"name": "body",
@ -915,7 +915,7 @@
"SessionService"
],
"summary": "Delete an existing JWT cookie if using HTTP.",
"operationId": "DeleteMixin6",
"operationId": "DeleteMixin7",
"responses": {
"200": {
"description": "(empty)",
@ -981,6 +981,39 @@
}
}
},
"/api/v1/users/{name}/password": {
"put": {
"tags": [
"UsersService"
],
"summary": "Update updates an application",
"operationId": "UpdatePassword",
"parameters": [
{
"type": "string",
"name": "name",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/usersBody"
}
}
],
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/usersUpdatePasswordResponse"
}
}
}
}
},
"/api/version": {
"get": {
"tags": [
@ -1313,6 +1346,20 @@
}
}
},
"usersBody": {
"type": "object",
"properties": {
"currentPassword": {
"type": "string"
},
"newPassword": {
"type": "string"
}
}
},
"usersUpdatePasswordResponse": {
"type": "object"
},
"v1Event": {
"description": "Event is a report of an event somewhere in the cluster.",
"type": "object",

52
server/users/users.go Normal file
View file

@ -0,0 +1,52 @@
package users
import (
"github.com/argoproj/argo-cd/util/password"
"github.com/argoproj/argo-cd/util/session"
"github.com/argoproj/argo-cd/util/settings"
"golang.org/x/net/context"
)
// Server provides a Session service
type Server struct {
sessionMgr *session.SessionManager
settingsMgr *settings.SettingsManager
}
// NewServer returns a new instance of the Session service
func NewServer(sessionMgr *session.SessionManager, settingsMgr *settings.SettingsManager) *Server {
return &Server{
sessionMgr: sessionMgr,
settingsMgr: settingsMgr,
}
}
//UpdatePassword is used to Update a User's Passwords
func (s *Server) UpdatePassword(ctx context.Context, q *UpdatePasswordRequest) (*UpdatePasswordResponse, error) {
cdSettings, err := s.settingsMgr.GetSettings()
if err != nil {
return nil, err
}
err = s.sessionMgr.VerifyUsernamePassword(q.Name, q.Body.GetCurrentPassword())
if err != nil {
return nil, err
}
hashedPassword, err := password.HashPassword(q.Body.GetNewPassword())
if err != nil {
return nil, err
}
cdSettings.LocalUsers[q.Name] = hashedPassword
err = s.settingsMgr.SaveSettings(cdSettings)
if err != nil {
return nil, err
}
return nil, nil
}

712
server/users/users.pb.go Normal file
View file

@ -0,0 +1,712 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: server/users/users.proto
/*
Package users is a generated protocol buffer package.
It is generated from these files:
server/users/users.proto
It has these top-level messages:
Body
UpdatePasswordRequest
UpdatePasswordResponse
*/
package users
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import _ "google.golang.org/genproto/googleapis/api/annotations"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type Body struct {
CurrentPassword string `protobuf:"bytes,2,opt,name=currentPassword,proto3" json:"currentPassword,omitempty"`
NewPassword string `protobuf:"bytes,3,opt,name=newPassword,proto3" json:"newPassword,omitempty"`
}
func (m *Body) Reset() { *m = Body{} }
func (m *Body) String() string { return proto.CompactTextString(m) }
func (*Body) ProtoMessage() {}
func (*Body) Descriptor() ([]byte, []int) { return fileDescriptorUsers, []int{0} }
func (m *Body) GetCurrentPassword() string {
if m != nil {
return m.CurrentPassword
}
return ""
}
func (m *Body) GetNewPassword() string {
if m != nil {
return m.NewPassword
}
return ""
}
type UpdatePasswordRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Body *Body `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
}
func (m *UpdatePasswordRequest) Reset() { *m = UpdatePasswordRequest{} }
func (m *UpdatePasswordRequest) String() string { return proto.CompactTextString(m) }
func (*UpdatePasswordRequest) ProtoMessage() {}
func (*UpdatePasswordRequest) Descriptor() ([]byte, []int) { return fileDescriptorUsers, []int{1} }
func (m *UpdatePasswordRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *UpdatePasswordRequest) GetBody() *Body {
if m != nil {
return m.Body
}
return nil
}
type UpdatePasswordResponse struct {
}
func (m *UpdatePasswordResponse) Reset() { *m = UpdatePasswordResponse{} }
func (m *UpdatePasswordResponse) String() string { return proto.CompactTextString(m) }
func (*UpdatePasswordResponse) ProtoMessage() {}
func (*UpdatePasswordResponse) Descriptor() ([]byte, []int) { return fileDescriptorUsers, []int{2} }
func init() {
proto.RegisterType((*Body)(nil), "users.Body")
proto.RegisterType((*UpdatePasswordRequest)(nil), "users.UpdatePasswordRequest")
proto.RegisterType((*UpdatePasswordResponse)(nil), "users.UpdatePasswordResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for UsersService service
type UsersServiceClient interface {
// Update updates an application
UpdatePassword(ctx context.Context, in *UpdatePasswordRequest, opts ...grpc.CallOption) (*UpdatePasswordResponse, error)
}
type usersServiceClient struct {
cc *grpc.ClientConn
}
func NewUsersServiceClient(cc *grpc.ClientConn) UsersServiceClient {
return &usersServiceClient{cc}
}
func (c *usersServiceClient) UpdatePassword(ctx context.Context, in *UpdatePasswordRequest, opts ...grpc.CallOption) (*UpdatePasswordResponse, error) {
out := new(UpdatePasswordResponse)
err := grpc.Invoke(ctx, "/users.UsersService/UpdatePassword", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for UsersService service
type UsersServiceServer interface {
// Update updates an application
UpdatePassword(context.Context, *UpdatePasswordRequest) (*UpdatePasswordResponse, error)
}
func RegisterUsersServiceServer(s *grpc.Server, srv UsersServiceServer) {
s.RegisterService(&_UsersService_serviceDesc, srv)
}
func _UsersService_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdatePasswordRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UsersServiceServer).UpdatePassword(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/users.UsersService/UpdatePassword",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UsersServiceServer).UpdatePassword(ctx, req.(*UpdatePasswordRequest))
}
return interceptor(ctx, in, info, handler)
}
var _UsersService_serviceDesc = grpc.ServiceDesc{
ServiceName: "users.UsersService",
HandlerType: (*UsersServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "UpdatePassword",
Handler: _UsersService_UpdatePassword_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "server/users/users.proto",
}
func (m *Body) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Body) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.CurrentPassword) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintUsers(dAtA, i, uint64(len(m.CurrentPassword)))
i += copy(dAtA[i:], m.CurrentPassword)
}
if len(m.NewPassword) > 0 {
dAtA[i] = 0x1a
i++
i = encodeVarintUsers(dAtA, i, uint64(len(m.NewPassword)))
i += copy(dAtA[i:], m.NewPassword)
}
return i, nil
}
func (m *UpdatePasswordRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *UpdatePasswordRequest) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Name) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintUsers(dAtA, i, uint64(len(m.Name)))
i += copy(dAtA[i:], m.Name)
}
if m.Body != nil {
dAtA[i] = 0x12
i++
i = encodeVarintUsers(dAtA, i, uint64(m.Body.Size()))
n1, err := m.Body.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
}
return i, nil
}
func (m *UpdatePasswordResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *UpdatePasswordResponse) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
return i, nil
}
func encodeVarintUsers(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *Body) Size() (n int) {
var l int
_ = l
l = len(m.CurrentPassword)
if l > 0 {
n += 1 + l + sovUsers(uint64(l))
}
l = len(m.NewPassword)
if l > 0 {
n += 1 + l + sovUsers(uint64(l))
}
return n
}
func (m *UpdatePasswordRequest) Size() (n int) {
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovUsers(uint64(l))
}
if m.Body != nil {
l = m.Body.Size()
n += 1 + l + sovUsers(uint64(l))
}
return n
}
func (m *UpdatePasswordResponse) Size() (n int) {
var l int
_ = l
return n
}
func sovUsers(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozUsers(x uint64) (n int) {
return sovUsers(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Body) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Body: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Body: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CurrentPassword", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthUsers
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.CurrentPassword = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field NewPassword", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthUsers
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.NewPassword = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipUsers(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthUsers
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *UpdatePasswordRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: UpdatePasswordRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: UpdatePasswordRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthUsers
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthUsers
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Body == nil {
m.Body = &Body{}
}
if err := m.Body.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipUsers(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthUsers
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *UpdatePasswordResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUsers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: UpdatePasswordResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: UpdatePasswordResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipUsers(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthUsers
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipUsers(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowUsers
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowUsers
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowUsers
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthUsers
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowUsers
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipUsers(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthUsers = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowUsers = fmt.Errorf("proto: integer overflow")
)
func init() { proto.RegisterFile("server/users/users.proto", fileDescriptorUsers) }
var fileDescriptorUsers = []byte{
// 313 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x51, 0x41, 0x4b, 0xf3, 0x40,
0x10, 0x65, 0xfb, 0xf5, 0x13, 0xdc, 0x8a, 0xc2, 0xa2, 0x12, 0x42, 0x5b, 0x4b, 0x40, 0x28, 0x88,
0x5d, 0xac, 0xe0, 0xc1, 0x63, 0xcf, 0x1e, 0x24, 0xd2, 0x8b, 0xb7, 0x6d, 0x32, 0xc4, 0x88, 0xdd,
0x89, 0xbb, 0x9b, 0x96, 0x2a, 0x5e, 0x3c, 0x7a, 0xf5, 0x4f, 0x79, 0x14, 0xfc, 0x03, 0x12, 0xfc,
0x21, 0x92, 0x49, 0x94, 0x5a, 0xf4, 0xb2, 0xcc, 0xbe, 0x79, 0xf3, 0xf6, 0xed, 0x3c, 0xee, 0x59,
0x30, 0x33, 0x30, 0x32, 0xb7, 0x60, 0x6c, 0x75, 0x0e, 0x32, 0x83, 0x0e, 0xc5, 0x7f, 0xba, 0xf8,
0xdb, 0x09, 0x26, 0x48, 0x88, 0x2c, 0xab, 0xaa, 0xe9, 0xb7, 0x13, 0xc4, 0xe4, 0x06, 0xa4, 0xca,
0x52, 0xa9, 0xb4, 0x46, 0xa7, 0x5c, 0x8a, 0xba, 0x1e, 0x0d, 0x42, 0xde, 0x1c, 0x61, 0xbc, 0x10,
0x7d, 0xbe, 0x15, 0xe5, 0xc6, 0x80, 0x76, 0xe7, 0xca, 0xda, 0x39, 0x9a, 0xd8, 0x6b, 0xf4, 0x58,
0x7f, 0x3d, 0x5c, 0x85, 0x45, 0x8f, 0xb7, 0x34, 0xcc, 0xbf, 0x59, 0xff, 0x88, 0xb5, 0x0c, 0x05,
0x67, 0x7c, 0x67, 0x9c, 0xc5, 0xca, 0xc1, 0x17, 0x12, 0xc2, 0x6d, 0x0e, 0xd6, 0x09, 0xc1, 0x9b,
0x5a, 0x4d, 0xc1, 0x63, 0x34, 0x43, 0xb5, 0xd8, 0xe3, 0xcd, 0x09, 0xc6, 0x0b, 0x7a, 0xad, 0x35,
0x6c, 0x0d, 0xaa, 0x7f, 0x95, 0x9e, 0x42, 0x6a, 0x04, 0x1e, 0xdf, 0x5d, 0x55, 0xb3, 0x19, 0x6a,
0x0b, 0xc3, 0x27, 0xc6, 0x37, 0xc6, 0x25, 0xfd, 0x02, 0xcc, 0x2c, 0x8d, 0x40, 0xdc, 0xf1, 0xcd,
0x9f, 0x54, 0xd1, 0xae, 0xf5, 0x7e, 0xf5, 0xe3, 0x77, 0xfe, 0xe8, 0x56, 0xfa, 0xc1, 0xc1, 0xe3,
0xdb, 0xc7, 0x73, 0x63, 0xdf, 0xef, 0xd0, 0xee, 0x66, 0x47, 0xf5, 0xe6, 0xef, 0x4b, 0xdf, 0x0f,
0x32, 0xab, 0xe9, 0xa7, 0x64, 0x73, 0x74, 0xf2, 0x52, 0x74, 0xd9, 0x6b, 0xd1, 0x65, 0xef, 0x45,
0x97, 0x5d, 0xf6, 0x93, 0xd4, 0x5d, 0xe5, 0x93, 0x41, 0x84, 0x53, 0xa9, 0x0c, 0xa5, 0x72, 0x4d,
0xc5, 0x61, 0x14, 0xcb, 0xe5, 0x1c, 0x27, 0x6b, 0x94, 0xc3, 0xf1, 0x67, 0x00, 0x00, 0x00, 0xff,
0xff, 0xcd, 0xc0, 0x6e, 0xa0, 0xde, 0x01, 0x00, 0x00,
}

138
server/users/users.pb.gw.go Normal file
View file

@ -0,0 +1,138 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: server/users/users.proto
/*
Package users is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package users
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
)
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
func request_UsersService_UpdatePassword_0(ctx context.Context, marshaler runtime.Marshaler, client UsersServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdatePasswordRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Body); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := client.UpdatePassword(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterUsersServiceHandlerFromEndpoint is same as RegisterUsersServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterUsersServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterUsersServiceHandler(ctx, mux, conn)
}
// RegisterUsersServiceHandler registers the http handlers for service UsersService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterUsersServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterUsersServiceHandlerClient(ctx, mux, NewUsersServiceClient(conn))
}
// RegisterUsersServiceHandler registers the http handlers for service UsersService to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "UsersServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "UsersServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "UsersServiceClient" to call the correct interceptors.
func RegisterUsersServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UsersServiceClient) error {
mux.Handle("PUT", pattern_UsersService_UpdatePassword_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UsersService_UpdatePassword_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_UsersService_UpdatePassword_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_UsersService_UpdatePassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "users", "name", "password"}, ""))
)
var (
forward_UsersService_UpdatePassword_0 = runtime.ForwardResponseMessage
)

37
server/users/users.proto Normal file
View file

@ -0,0 +1,37 @@
syntax = "proto3";
option go_package = "github.com/argoproj/argo-cd/server/users";
//Users Service
//
//
//Users Service API updates ArgoCD users settings
package users;
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
message Body {
string currentPassword = 2;
string newPassword = 3;
}
message UpdatePasswordRequest {
string name = 1;
Body body = 2;
}
message UpdatePasswordResponse {}
service UsersService {
// Update updates an application
rpc UpdatePassword(UpdatePasswordRequest) returns (UpdatePasswordResponse) {
option (google.api.http) = {
put: "/api/v1/users/{name}/password"
body: "body"
};
}
}

View file

@ -388,13 +388,13 @@ func (mgr *SettingsManager) notifySubscribers() {
}
}
func readAndConfirmPassword() string {
func ReadAndConfirmPassword() string {
for {
fmt.Print("*** Enter an admin password: ")
fmt.Print("*** Enter a new password ")
password, err := terminal.ReadPassword(syscall.Stdin)
errors.CheckError(err)
fmt.Print("\n")
fmt.Print("*** Confirm the admin password: ")
fmt.Print("*** Confirm the new password ")
confirmPassword, err := terminal.ReadPassword(syscall.Stdin)
errors.CheckError(err)
fmt.Print("\n")
@ -426,7 +426,7 @@ func UpdateSettings(defaultPassword string, settingsMgr *SettingsManager, update
if _, ok := cdSettings.LocalUsers[common.ArgoCDAdminUsername]; !ok || updateSuperuser {
passwordRaw := defaultPassword
if passwordRaw == "" {
passwordRaw = readAndConfirmPassword()
passwordRaw = ReadAndConfirmPassword()
}
log.Infof("password set to %s", passwordRaw)
hashedPassword, err := password.HashPassword(passwordRaw)