Newer
Older
package server
import (
"context"
"time"
apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
"code.fbi.h-da.de/danet/gosdn/controller/metrics"
"code.fbi.h-da.de/danet/gosdn/controller/rbac"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// User holds a JWTManager and represents a UserServiceServer.
type User struct {
apb.UnimplementedUserServiceServer
jwtManager *rbac.JWTManager
}
// NewUserServer receives a JWTManager and returns a new UserServer.
func NewUserServer(jwtManager *rbac.JWTManager) *User {
return &User{
jwtManager: jwtManager,
}
}
// CreateUsers creates new users, can be 1 or more
func (u User) CreateUsers(ctx context.Context, request *apb.CreateUsersRequest) (*apb.CreateUsersResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "post"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
for _, u := range request.User {
roles := map[string]string{}
for key, elem := range u.Roles {
_, err := uuid.Parse(key)
if err != nil {
return nil, handleRPCError(labels, err)
}
roles[key] = elem
}
// Generate a salt that is 16 characters long with 3 digits, 0 symbols,
// allowing upper and lower case letters, disallowing repeat characters.
salt, err := password.Generate(16, 3, 0, true, false)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
hashedPassword := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(u.Password), []byte(salt), 1, 64*1024, 4, 32))
user := rbac.NewUser(uuid.New(), u.Name, roles, string(hashedPassword), u.Token, salt)
err = userService.Add(user)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
}
return &apb.CreateUsersResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
}, nil
}
// GetUser returns one user by name.
func (u User) GetUser(ctx context.Context, request *apb.GetUserRequest) (*apb.GetUserResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
userData, err := userService.Get(store.Query{Name: request.Name})
if err != nil {
return nil, err
}
user := &apb.User{
Id: userData.ID().String(),
Name: userData.Name(),
Roles: userData.GetRoles(),
}
return &apb.GetUserResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
User: user,
}, nil
}
// GetUsers returns all availbale users
func (u User) GetUsers(ctx context.Context, request *apb.GetUsersRequest) (*apb.GetUsersResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
if err != nil {
return nil, err
}
users := []*apb.User{}
for _, u := range userList {
users = append(users, &apb.User{
Id: u.ID().String(),
Name: u.Name(),
Roles: u.GetRoles(),
})
}
return &apb.GetUsersResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
User: users,
}, nil
}
// UpdateUsers updates the user data of one or more users provided in the request
func (u User) UpdateUsers(ctx context.Context, request *apb.UpdateUsersRequest) (*apb.UpdateUsersResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "post"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
for _, u := range request.User {
uid, err := uuid.Parse(u.Id)
if err != nil {
return nil, handleRPCError(labels, err)
}
storedUser, err := userService.Get(store.Query{ID: uid})
if err != nil {
return nil, status.Errorf(codes.Canceled, "user not found %v", err)
}
hashedPassword := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(u.Password), []byte(storedUser.GetSalt()), 1, 64*1024, 4, 32))
userToUpdate := rbac.NewUser(uid, u.Name, u.Roles, string(hashedPassword), u.Token, storedUser.GetSalt())
if err != nil {
return nil, status.Errorf(codes.Aborted, "could not update user %v", err)
}
}
return &apb.UpdateUsersResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
}, nil
}
// DeleteUsers deletes one or more users provided in the request
func (u User) DeleteUsers(ctx context.Context, request *apb.DeleteUsersRequest) (*apb.DeleteUsersResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
for _, u := range request.Username {
userToDelete, err := userService.Get(store.Query{Name: u})
if err != nil {
return nil, status.Errorf(codes.Canceled, "user not found %v", err)
}
if err != nil {
return nil, status.Errorf(codes.Aborted, "error deleting user %v", err)
}
}
return &apb.DeleteUsersResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
}, nil
}
func (u User) isValidUser(user rbac.User) (bool, error) {
storedUser, err := userService.Get(store.Query{Name: user.Name()})
if err != nil {
return false, err
} else if storedUser == nil {
return false, status.Errorf(codes.Aborted, "no user object")
}
if storedUser.Name() == user.Name() {
salt := storedUser.GetSalt()
hashedPasswordFromLogin := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(user.GetPassword()), []byte(salt), 1, 64*1024, 4, 32))
if storedUser.GetPassword() == hashedPasswordFromLogin {
return true, nil
}
}
return false, status.Errorf(codes.Unauthenticated, "incorrect user name or password")
}