2025-09-05 15:48:11 +08:00
import datetime
2025-06-26 17:56:55 +08:00
from tzlocal import get_localzone
2025-06-02 12:04:01 +08:00
from functools import wraps
2025-06-28 18:13:26 +08:00
from flask import Blueprint , render_template , abort , request , Flask , current_app , session , redirect , url_for
2025-05-29 16:23:20 +08:00
import os
2025-06-02 12:04:01 +08:00
from modules . WireguardConfiguration import WireguardConfiguration
from modules . DashboardConfig import DashboardConfig
2025-09-05 15:48:11 +08:00
from modules . Email import EmailSender
2025-05-29 16:23:20 +08:00
2025-05-31 22:59:46 +08:00
def ResponseObject ( status = True , message = None , data = None , status_code = 200 ) - > Flask . response_class :
response = Flask . make_response ( current_app , {
" status " : status ,
" message " : message ,
" data " : data
} )
response . status_code = status_code
response . content_type = " application/json "
return response
2025-08-11 17:29:15 +08:00
2025-05-31 22:59:46 +08:00
2025-07-10 23:39:21 +08:00
from modules . DashboardClients import DashboardClients
def createClientBlueprint ( wireguardConfigurations : dict [ WireguardConfiguration ] , dashboardConfig : DashboardConfig , dashboardClients : DashboardClients ) :
2025-07-02 18:51:58 +08:00
2025-08-17 16:48:31 +08:00
client = Blueprint ( ' client ' , __name__ , template_folder = os . path . abspath ( " ./static/dist/WGDashboardClient " ) )
2025-06-02 12:04:01 +08:00
prefix = f ' { dashboardConfig . GetConfig ( " Server " , " app_prefix " ) [ 1 ] } /client '
2025-08-11 17:29:15 +08:00
def login_required ( f ) :
@wraps ( f )
def func ( * args , * * kwargs ) :
if session . get ( " Email " ) is None or session . get ( " TotpVerified " ) is None or not session . get ( " TotpVerified " ) or session . get ( " Role " ) != " client " :
return ResponseObject ( False , " Unauthorized access. " , data = None , status_code = 401 )
if not dashboardClients . GetClient ( session . get ( " ClientID " ) ) :
session . clear ( )
return ResponseObject ( False , " Unauthorized access. " , data = None , status_code = 401 )
return f ( * args , * * kwargs )
return func
2025-06-02 12:04:01 +08:00
@client.before_request
def clientBeforeRequest ( ) :
2025-09-05 16:39:59 +08:00
if not dashboardConfig . GetConfig ( " Clients " , " enable " ) [ 1 ] :
abort ( 404 )
2025-06-02 12:04:01 +08:00
if request . method . lower ( ) == ' options ' :
return ResponseObject ( True )
@client.post ( f ' { prefix } /api/signup ' )
def ClientAPI_SignUp ( ) :
2025-09-05 15:48:11 +08:00
data = request . get_json ( )
2025-07-10 23:39:21 +08:00
status , msg = dashboardClients . SignUp ( * * data )
2025-06-02 19:23:04 +08:00
return ResponseObject ( status , msg )
2025-06-29 16:11:05 +08:00
@client.get ( f ' { prefix } /api/signin/oidc/providers ' )
def ClientAPI_SignIn_OIDC_GetProviders ( ) :
2025-08-02 21:58:09 +08:00
_ , oidc = dashboardConfig . GetConfig ( " OIDC " , " client_enable " )
if not oidc :
return ResponseObject ( status = False , message = " OIDC is disabled " )
2025-07-10 23:39:21 +08:00
return ResponseObject ( data = dashboardClients . OIDC . GetProviders ( ) )
2025-06-02 12:04:01 +08:00
2025-06-29 16:11:05 +08:00
@client.post ( f ' { prefix } /api/signin/oidc ' )
def ClientAPI_SignIn_OIDC ( ) :
2025-08-02 21:58:09 +08:00
_ , oidc = dashboardConfig . GetConfig ( " OIDC " , " client_enable " )
if not oidc :
return ResponseObject ( status = False , message = " OIDC is disabled " )
2025-09-05 15:48:11 +08:00
data = request . get_json ( )
2025-07-10 23:39:21 +08:00
status , oidcData = dashboardClients . SignIn_OIDC ( * * data )
2025-06-29 16:11:05 +08:00
if not status :
return ResponseObject ( status , oidcData )
session [ ' Email ' ] = oidcData . get ( ' email ' )
2025-07-03 19:20:01 +08:00
session [ ' Role ' ] = ' client '
session [ ' TotpVerified ' ] = True
2025-06-29 16:11:05 +08:00
2025-06-28 18:13:26 +08:00
return ResponseObject ( )
2025-06-02 19:23:04 +08:00
@client.post ( f ' { prefix } /api/signin ' )
def ClientAPI_SignIn ( ) :
2025-09-05 15:48:11 +08:00
data = request . get_json ( )
2025-07-10 23:39:21 +08:00
status , msg = dashboardClients . SignIn ( * * data )
2025-06-03 03:02:06 +08:00
if status :
2025-06-29 16:11:05 +08:00
session [ ' Email ' ] = data . get ( ' Email ' )
2025-07-03 19:20:01 +08:00
session [ ' Role ' ] = ' client '
session [ ' TotpVerified ' ] = False
2025-06-03 03:02:06 +08:00
return ResponseObject ( status , msg )
2025-06-03 17:18:18 +08:00
2025-09-05 15:48:11 +08:00
@client.post ( f ' { prefix } /api/resetPassword/generateResetToken ' )
def ClientAPI_ResetPassword_GenerateResetToken ( ) :
date = datetime . datetime . now ( tz = datetime . timezone . utc ) . strftime ( ' % Y- % m- %d % H: % M: % S UTC ' )
emailSender = EmailSender ( dashboardConfig )
if not emailSender . ready ( ) :
return ResponseObject ( False , " We can ' t send you an email due to your Administrator has not setup email service. Please contact your administrator. " )
data = request . get_json ( )
email = data . get ( ' Email ' , None )
if not email :
return ResponseObject ( False , " Please provide a valid Email " )
u = dashboardClients . SignIn_UserExistence ( email )
if not u :
return ResponseObject ( False , " Please provide a valid Email " )
token = dashboardClients . GenerateClientPasswordResetToken ( u . get ( ' ClientID ' ) )
status , msg = emailSender . send (
email , " [WGDashboard | Client] Reset Password " ,
f " Hi { email } , \n \n It looks like you ' re trying to reset your password at { date } \n \n Enter this 6 digits code on the Forgot Password to continue: \n \n { token } \n \n This code will expire in 30 minutes for your security. If you didn’ t request a password reset, you can safely ignore this email—your current password will remain unchanged. \n \n If you need help, feel free to contact support. \n \n Best regards, \n \n WGDashboard "
)
return ResponseObject ( status , msg )
@client.post ( f ' { prefix } /api/resetPassword/validateResetToken ' )
def ClientAPI_ResetPassword_ValidateResetToken ( ) :
data = request . get_json ( )
email = data . get ( ' Email ' , None )
token = data . get ( ' Token ' , None )
if not all ( [ email , token ] ) :
return ResponseObject ( False , " Please provide a valid Email " )
u = dashboardClients . SignIn_UserExistence ( email )
if not u :
return ResponseObject ( False , " Please provide a valid Email " )
return ResponseObject ( status = dashboardClients . ValidateClientPasswordResetToken ( u . get ( ' ClientID ' ) , token ) )
@client.post ( f ' { prefix } /api/resetPassword ' )
def ClientAPI_ResetPassword ( ) :
data = request . get_json ( )
email = data . get ( ' Email ' , None )
token = data . get ( ' Token ' , None )
password = data . get ( ' Password ' , None )
confirmPassword = data . get ( ' ConfirmPassword ' , None )
if not all ( [ email , token , password , confirmPassword ] ) :
return ResponseObject ( False , " Please provide a valid Email " )
u = dashboardClients . SignIn_UserExistence ( email )
if not u :
return ResponseObject ( False , " Please provide a valid Email " )
if not dashboardClients . ValidateClientPasswordResetToken ( u . get ( ' ClientID ' ) , token ) :
return ResponseObject ( False , " Verification code is either invalid or expired " )
status , msg = dashboardClients . ResetClientPassword ( u . get ( ' ClientID ' ) , password , confirmPassword )
dashboardClients . RevokeClientPasswordResetToken ( u . get ( ' ClientID ' ) , token )
return ResponseObject ( status , msg )
2025-06-03 17:18:18 +08:00
@client.get ( f ' { prefix } /api/signout ' )
def ClientAPI_SignOut ( ) :
2025-07-03 19:20:01 +08:00
if session . get ( " SignInMethod " ) == " OIDC " :
2025-07-10 23:39:21 +08:00
dashboardClients . SignOut_OIDC ( )
2025-07-03 19:20:01 +08:00
session . clear ( )
2025-06-03 17:18:18 +08:00
return ResponseObject ( True )
2025-06-03 03:02:06 +08:00
@client.get ( f ' { prefix } /api/signin/totp ' )
def ClientAPI_SignIn_TOTP ( ) :
token = request . args . get ( ' Token ' , None )
if not token :
return ResponseObject ( False , " Please provide TOTP token " )
2025-07-10 23:39:21 +08:00
status , msg = dashboardClients . SignIn_GetTotp ( token )
2025-06-02 12:04:01 +08:00
return ResponseObject ( status , msg )
2025-06-02 19:23:04 +08:00
2025-06-03 03:02:06 +08:00
@client.post ( f ' { prefix } /api/signin/totp ' )
def ClientAPI_SignIn_ValidateTOTP ( ) :
2025-09-05 15:48:11 +08:00
data = request . get_json ( )
2025-06-03 03:02:06 +08:00
token = data . get ( ' Token ' , None )
userProvidedTotp = data . get ( ' UserProvidedTOTP ' , None )
if not all ( [ token , userProvidedTotp ] ) :
return ResponseObject ( False , " Please fill in all fields " )
2025-07-10 23:39:21 +08:00
status , msg = dashboardClients . SignIn_GetTotp ( token , userProvidedTotp )
2025-06-03 03:02:06 +08:00
if status :
2025-06-29 16:11:05 +08:00
if session . get ( ' Email ' ) is None :
2025-06-03 03:02:06 +08:00
return ResponseObject ( False , " Sign in status is invalid " , status_code = 401 )
2025-07-03 19:20:01 +08:00
session [ ' TotpVerified ' ] = True
2025-08-11 17:29:15 +08:00
profile = dashboardClients . GetClientProfile ( session . get ( " ClientID " ) )
2025-06-06 15:49:55 +08:00
return ResponseObject ( True , data = {
2025-06-29 16:11:05 +08:00
" Email " : session . get ( ' Email ' ) ,
2025-08-11 17:29:15 +08:00
" Profile " : profile
2025-06-06 15:49:55 +08:00
} )
2025-06-03 03:02:06 +08:00
return ResponseObject ( status , msg )
2025-06-02 19:23:04 +08:00
@client.get ( prefix )
def ClientIndex ( ) :
return render_template ( ' client.html ' )
2025-06-02 12:04:01 +08:00
2025-06-26 17:56:55 +08:00
@client.get ( f ' { prefix } /api/serverInformation ' )
def ClientAPI_ServerInformation ( ) :
return ResponseObject ( data = {
" ServerTimezone " : str ( get_localzone ( ) )
} )
2025-06-03 03:02:06 +08:00
@client.get ( f ' { prefix } /api/validateAuthentication ' )
@login_required
def ClientAPI_ValidateAuthentication ( ) :
return ResponseObject ( True )
@client.get ( f ' { prefix } /api/configurations ' )
@login_required
def ClientAPI_Configurations ( ) :
2025-07-10 23:39:21 +08:00
return ResponseObject ( True , data = dashboardClients . GetClientAssignedPeers ( session [ ' ClientID ' ] ) )
2025-06-03 03:02:06 +08:00
2025-06-19 16:51:59 +08:00
@client.get ( f ' { prefix } /api/settings/getClientProfile ' )
@login_required
def ClientAPI_Settings_GetClientProfile ( ) :
2025-07-08 16:32:08 +08:00
return ResponseObject ( data = {
" Email " : session . get ( " Email " ) ,
" SignInMethod " : session . get ( " SignInMethod " ) ,
2025-07-10 23:39:21 +08:00
" Profile " : dashboardClients . GetClientProfile ( session . get ( " ClientID " ) )
2025-07-08 16:32:08 +08:00
} )
2025-06-19 16:51:59 +08:00
2025-06-20 16:00:19 +08:00
@client.post ( f ' { prefix } /api/settings/updatePassword ' )
@login_required
def ClientAPI_Settings_UpdatePassword ( ) :
2025-08-11 17:29:15 +08:00
data = request . get_json ( )
2025-09-05 15:48:11 +08:00
status , message = dashboardClients . UpdateClientPassword ( session [ ' ClientID ' ] , * * data )
2025-06-20 16:00:19 +08:00
2025-09-05 15:48:11 +08:00
return ResponseObject ( status , message )
2025-06-20 16:00:19 +08:00
2025-06-02 12:04:01 +08:00
return client