2020-04-17 21:25:35 +02:00
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
2020-12-23 16:41:52 +01:00
// it under the terms of the GNU Affero General Public Licensee as published by
2020-04-17 21:25:35 +02:00
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2020-12-23 16:41:52 +01:00
// GNU Affero General Public Licensee for more details.
2020-04-17 21:25:35 +02:00
//
2020-12-23 16:41:52 +01:00
// You should have received a copy of the GNU Affero General Public Licensee
2020-04-17 21:25:35 +02:00
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package v1
import (
"bytes"
2020-10-11 22:10:03 +02:00
"fmt"
"image/jpeg"
"net/http"
2020-12-23 16:32:28 +01:00
"code.vikunja.io/api/pkg/db"
2020-04-17 21:25:35 +02:00
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
)
// UserTOTPEnroll is the handler to enroll a user into totp
// @Summary Enroll a user into totp
// @Description Creates an initial setup for the user in the db. After this step, the user needs to verify they have a working totp setup with the "enable totp" endpoint.
// @tags user
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Success 200 {object} user.TOTP
2020-06-28 16:25:46 +02:00
// @Failure 400 {object} web.HTTPError "Something's invalid."
// @Failure 404 {object} web.HTTPError "User does not exist."
2020-04-17 21:25:35 +02:00
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user/settings/totp/enroll [post]
func UserTOTPEnroll ( c echo . Context ) error {
u , err := user . GetCurrentUser ( c )
if err != nil {
return handler . HandleHTTPError ( err , c )
}
2020-12-23 16:32:28 +01:00
s := db . NewSession ( )
defer s . Close ( )
t , err := user . EnrollTOTP ( s , u )
2020-04-17 21:25:35 +02:00
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
return handler . HandleHTTPError ( err , c )
}
if err := s . Commit ( ) ; err != nil {
_ = s . Rollback ( )
2020-04-17 21:25:35 +02:00
return handler . HandleHTTPError ( err , c )
}
return c . JSON ( http . StatusOK , t )
}
// UserTOTPEnable is the handler to enable totp for a user
// @Summary Enable a previously enrolled totp setting.
// @Description Enables a previously enrolled totp setting by providing a totp passcode.
// @tags user
// @Accept json
// @Produce json
// @Param totp body user.TOTPPasscode true "The totp passcode."
// @Security JWTKeyAuth
// @Success 200 {object} models.Message "Successfully enabled"
2020-06-28 16:25:46 +02:00
// @Failure 400 {object} web.HTTPError "Something's invalid."
// @Failure 404 {object} web.HTTPError "User does not exist."
// @Failure 412 {object} web.HTTPError "TOTP is not enrolled."
2020-04-17 21:25:35 +02:00
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user/settings/totp/enable [post]
func UserTOTPEnable ( c echo . Context ) error {
u , err := user . GetCurrentUser ( c )
if err != nil {
return handler . HandleHTTPError ( err , c )
}
passcode := & user . TOTPPasscode {
User : u ,
}
if err := c . Bind ( passcode ) ; err != nil {
log . Debugf ( "Invalid model error. Internal error was: %s" , err . Error ( ) )
if he , is := err . ( * echo . HTTPError ) ; is {
return echo . NewHTTPError ( http . StatusBadRequest , fmt . Sprintf ( "Invalid model provided. Error was: %s" , he . Message ) )
}
2020-05-29 22:15:21 +02:00
return echo . NewHTTPError ( http . StatusBadRequest , "Invalid model provided." )
2020-04-17 21:25:35 +02:00
}
2020-12-23 16:32:28 +01:00
s := db . NewSession ( )
defer s . Close ( )
err = user . EnableTOTP ( s , passcode )
2020-04-17 21:25:35 +02:00
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
return handler . HandleHTTPError ( err , c )
}
if err := s . Commit ( ) ; err != nil {
_ = s . Rollback ( )
2020-04-17 21:25:35 +02:00
return handler . HandleHTTPError ( err , c )
}
return c . JSON ( http . StatusOK , models . Message { Message : "TOTP was enabled successfully." } )
}
2020-04-18 01:38:49 +02:00
// UserTOTPDisable disables totp settings for the current user.
// @Summary Disable totp settings
// @Description Disables any totp settings for the current user.
// @tags user
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param totp body user.Login true "The current user's password (only password is enough)."
// @Success 200 {object} models.Message "Successfully disabled"
2020-06-28 16:25:46 +02:00
// @Failure 400 {object} web.HTTPError "Something's invalid."
// @Failure 404 {object} web.HTTPError "User does not exist."
2020-04-18 01:38:49 +02:00
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user/settings/totp/disable [post]
func UserTOTPDisable ( c echo . Context ) error {
login := & user . Login { }
if err := c . Bind ( login ) ; err != nil {
log . Debugf ( "Invalid model error. Internal error was: %s" , err . Error ( ) )
if he , is := err . ( * echo . HTTPError ) ; is {
return echo . NewHTTPError ( http . StatusBadRequest , fmt . Sprintf ( "Invalid model provided. Error was: %s" , he . Message ) )
}
2020-05-29 22:15:21 +02:00
return echo . NewHTTPError ( http . StatusBadRequest , "Invalid model provided." )
2020-04-18 01:38:49 +02:00
}
u , err := user . GetCurrentUser ( c )
if err != nil {
return handler . HandleHTTPError ( err , c )
}
2020-12-23 16:32:28 +01:00
s := db . NewSession ( )
defer s . Close ( )
u , err = user . GetUserByID ( s , u . ID )
2020-04-18 01:38:49 +02:00
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
2020-04-18 01:38:49 +02:00
return handler . HandleHTTPError ( err , c )
}
err = user . CheckUserPassword ( u , login . Password )
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
2020-04-18 01:38:49 +02:00
return handler . HandleHTTPError ( err , c )
}
2020-12-23 16:32:28 +01:00
err = user . DisableTOTP ( s , u )
2020-04-18 01:38:49 +02:00
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
return handler . HandleHTTPError ( err , c )
}
if err := s . Commit ( ) ; err != nil {
_ = s . Rollback ( )
2020-04-18 01:38:49 +02:00
return handler . HandleHTTPError ( err , c )
}
return c . JSON ( http . StatusOK , models . Message { Message : "TOTP was enabled successfully." } )
}
2020-04-17 21:25:35 +02:00
// UserTOTPQrCode is the handler to show a qr code to enroll the user into totp
// @Summary Totp QR Code
// @Description Returns a qr code for easier setup at end user's devices.
// @tags user
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Success 200 {} string "The qr code as jpeg image"
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user/settings/totp/qrcode [get]
func UserTOTPQrCode ( c echo . Context ) error {
u , err := user . GetCurrentUser ( c )
if err != nil {
return handler . HandleHTTPError ( err , c )
}
2020-12-23 16:32:28 +01:00
s := db . NewSession ( )
defer s . Close ( )
qrcode , err := user . GetTOTPQrCodeForUser ( s , u )
2020-04-17 21:25:35 +02:00
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
2020-04-17 21:25:35 +02:00
return handler . HandleHTTPError ( err , c )
}
buff := & bytes . Buffer { }
err = jpeg . Encode ( buff , qrcode , nil )
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
return handler . HandleHTTPError ( err , c )
}
if err := s . Commit ( ) ; err != nil {
_ = s . Rollback ( )
2020-04-17 21:25:35 +02:00
return handler . HandleHTTPError ( err , c )
}
return c . Blob ( http . StatusOK , "image/jpeg" , buff . Bytes ( ) )
}
2020-04-18 00:22:59 +02:00
// UserTOTP returns the current totp implementation if any is enabled.
// @Summary Totp setting for the current user
// @Description Returns the current user totp setting or an error if it is not enabled.
// @tags user
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Success 200 {object} user.TOTP "The totp settings."
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user/settings/totp [get]
func UserTOTP ( c echo . Context ) error {
u , err := user . GetCurrentUser ( c )
if err != nil {
return handler . HandleHTTPError ( err , c )
}
2020-12-23 16:32:28 +01:00
s := db . NewSession ( )
defer s . Close ( )
t , err := user . GetTOTPForUser ( s , u )
2020-04-18 00:22:59 +02:00
if err != nil {
2020-12-23 16:32:28 +01:00
_ = s . Rollback ( )
return handler . HandleHTTPError ( err , c )
}
if err := s . Commit ( ) ; err != nil {
_ = s . Rollback ( )
2020-04-18 00:22:59 +02:00
return handler . HandleHTTPError ( err , c )
}
return c . JSON ( http . StatusOK , t )
}