Newer
Older
package server
import (
"context"
"time"
apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
rbacInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/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/prometheus/client_golang/prometheus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
// Auth holds a JWTManager and represents a AuthServiceServer.
type Auth struct {
apb.UnimplementedAuthServiceServer
jwtManager *rbac.JWTManager
userService rbacInterfaces.UserService
// NewAuthServer receives a JWTManager and a userService and returns a new Auth interface.
func NewAuthServer(jwtManager *rbac.JWTManager, userService rbacInterfaces.UserService) *Auth {
jwtManager: jwtManager,
userService: userService,
}
}
// Login logs a user in
func (s Auth) Login(ctx context.Context, request *apb.LoginRequest) (*apb.LoginResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "post"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
Password: request.Pwd,
}
// validation of credentials
return nil, err
}
// generate token, persist session and return to user
token, err := s.jwtManager.GenerateToken(user)
if err != nil {
return nil, err
}
userToUpdate, err := s.userService.Get(store.Query{Name: user.UserName})
if err != nil {
return nil, err
}
userToUpdate.SetToken(token)
err = s.userService.Update(userToUpdate)
if err != nil {
return nil, err
}
return &apb.LoginResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
// Logout logs a user out
func (s Auth) Logout(ctx context.Context, request *apb.LogoutRequest) (*apb.LogoutResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "post"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := s.handleLogout(ctx, request.Username)
if err != nil {
return nil, err
}
return &apb.LogoutResponse{
Timestamp: time.Now().UnixNano(),
Status: apb.Status_STATUS_OK,
}, nil
}
// isValidUser checks if the provided user name fits to a stored one and then checks if the provided password is correct.
func (s Auth) isValidUser(user rbac.User) error {
storedUser, err := s.userService.Get(store.Query{Name: user.Name()})
if storedUser.Name() == user.Name() {
102
103
104
105
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
138
139
140
141
err := s.isCorrectPassword(storedUser.GetPassword(), storedUser.GetSalt(), user.Password)
if err != nil {
return err
}
}
return nil
}
// isCorrectPassword checks if the provided password fits with the hashed user password taken from the storage.
func (s Auth) isCorrectPassword(storedPassword, salt, loginPassword string) error {
hashedPasswordFromLogin := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(loginPassword), []byte(salt), 1, 64*1024, 4, 32))
if storedPassword == hashedPasswordFromLogin {
return nil
}
return status.Errorf(codes.Unauthenticated, "incorrect user name or password")
}
// handleLogout checks if the provided user name matches with the one associated with token and
// replaces the stored token of the user with an empty string.
func (s Auth) handleLogout(ctx context.Context, userName string) error {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return status.Errorf(codes.Aborted, "metadata is not provided")
}
if len(md["authorize"]) > 0 {
token := md["authorize"][0]
claims, err := s.jwtManager.GetClaimsFromToken(token)
if err != nil {
return err
}
if claims.Username != userName {
return status.Errorf(codes.Aborted, "missing match of user associated to token and provided user name")
}
storedUser, err := s.userService.Get(store.Query{Name: userName})
if err != nil {
return err
}
if token != storedUser.GetToken() {
return status.Errorf(codes.Aborted, "missing match of token provied for user")
}
err = s.userService.Update(&rbac.User{UserID: storedUser.ID(),
UserName: storedUser.Name(),
Roles: storedUser.GetRoles(),
Password: storedUser.GetPassword(),
Token: " ",
Salt: storedUser.GetSalt(),
})
if err != nil {
return err