package grpc import ( "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "src.dualinventive.com/go/authentication-service/internal/authtokens" "src.dualinventive.com/go/authentication-service/internal/domain" "src.dualinventive.com/go/authentication-service/internal/pwreset" ) //Server is a structure of server instance type Server struct { Name string TokenService *authtokens.Service } //NewServer creates new instance of Server func NewServer( tokenService *authtokens.Service, ) (*Server, error) { server := &Server{ Name: "authentication-service-grpc", TokenService: tokenService, } return server, nil } //Login is used to create a new token for user func (s *Server) Login(ctx context.Context, req *LoginRequest) (*LoginResponse, error) { credentials := domain.Credentials{ User: req.Credentials.Username, CompanyCode: req.Credentials.CompanyCode, Password: req.Credentials.Password, } userAgent := getValue(ctx, "userAgent") token, err := s.TokenService.CreateToken(credentials, userAgent) if err != nil { return nil, mapError(err) } return &LoginResponse{Token: &Token{ Secret: token.Secret}}, nil } //Logout is used to remove the token for user func (s *Server) Logout(ctx context.Context, empty *Empty) (*Empty, error) { token := &domain.Token{Secret: getValue(ctx, "token")} err := s.TokenService.DeleteToken(token) if err != nil { return nil, mapError(err) } return &Empty{}, nil } //VerifyToken is used to retrieve user information based on the given token. func (s *Server) VerifyToken(ctx context.Context, req *VerifyTokenRequest) (*VerifyTokenResponse, error) { token := &domain.Token{Secret: getValue(ctx, "token")} _, err := s.TokenService.VerifyToken(token, req.Rights...) if err != nil { switch err.(type) { case *authtokens.ErrTokenNotFound: return &VerifyTokenResponse{Valid: false}, nil case *authtokens.ErrInvalidToken: return &VerifyTokenResponse{Valid: false}, nil case *authtokens.ErrUnauthorized: return &VerifyTokenResponse{Valid: false}, nil default: return nil, status.Error(codes.Internal, err.Error()) } } return &VerifyTokenResponse{Valid: true}, nil } //Me is used to retrieve user information based on the given token. func (s *Server) Me(ctx context.Context, empty *Empty) (*MeResponse, error) { token := &domain.Token{Secret: getValue(ctx, "token")} user, err := s.TokenService.GetUserByToken(token) if err != nil { return nil, mapError(err) } return &MeResponse{User: mapUser(user)}, nil } //RequestPasswordReset generates a unique password reset code and sends it to the users email address. func (s *Server) RequestPasswordReset(ctx context.Context, req *RequestPasswordResetRequest) (*Empty, error) { err := pwreset.RequestPasswordReset( s.TokenService.CredentialsRepository, s.TokenService.EmailSender, s.TokenService.TemplateRepository, s.TokenService.ResetCodeRepository, req.UserName, ) if err != nil { switch err.(type) { case *pwreset.ErrFailedToCreateResetCode: return nil, status.Error(codes.Internal, err.Error()) case *pwreset.ErrUserFetchFailed: return nil, status.Error(codes.Internal, err.Error()) case *pwreset.ErrFailedToSendEmail: return nil, status.Error(codes.Internal, err.Error()) default: return nil, status.Error(codes.InvalidArgument, err.Error()) } } return &Empty{}, nil } //RedeemPasswordReset will set a password of a user when the given username and password reset code a valid. func (s *Server) RedeemPasswordReset(ctx context.Context, req *RedeemPasswordResetRequest) (*Empty, error) { err := pwreset.RedeemPasswordReset( s.TokenService.CredentialsRepository, s.TokenService.ResetCodeRepository, req.UserName, req.ResetCode, req.Password, req.PasswordVerify, ) if err != nil { switch err.(type) { case *pwreset.ErrFailedToGetResetCode: return nil, status.Error(codes.Internal, err.Error()) case *pwreset.ErrChangePasswordFailed: return nil, status.Error(codes.Internal, err.Error()) default: return nil, status.Error(codes.InvalidArgument, err.Error()) } } return &Empty{}, nil } //ListTokens returns a list of active shielded tokens for the current user. func (s *Server) ListTokens(ctx context.Context, empty *Empty) (*ListTokensResponse, error) { token := &domain.Token{Secret: getValue(ctx, "token")} opaqueTokens, err := s.TokenService.GetOpaqueTokensByToken(token) if err != nil { return nil, mapError(err) } return &ListTokensResponse{Tokens: mapTokenList(opaqueTokens)}, nil } //DeleteToken removes a token by reference of it's opaque id. func (s *Server) DeleteToken(ctx context.Context, req *DeleteTokenRequest) (*Empty, error) { token := &domain.Token{Secret: getValue(ctx, "token")} err := s.TokenService.DeleteTokenByOpaqueID(token, req.OpaqueId) if err != nil { return nil, mapError(err) } return &Empty{}, nil } func getValue(ctx context.Context, s string) string { md, ok := metadata.FromIncomingContext(ctx) if !ok { return "" } raw := md.Get(s) if raw == nil { return "" } if len(raw) == 0 { return "" } return raw[0] }