2018-02-10 13:37:23 +00:00
package main
import (
2018-02-21 23:17:07 +00:00
"bytes"
2018-02-18 16:37:16 +00:00
"crypto/hmac"
"crypto/sha512"
2018-02-21 23:17:07 +00:00
"encoding/base64"
2018-02-10 13:37:23 +00:00
"encoding/json"
2018-02-18 13:41:06 +00:00
"errors"
"fmt"
2018-02-10 13:37:23 +00:00
"log"
2018-02-18 13:41:06 +00:00
"net"
2018-02-10 13:37:23 +00:00
"net/http"
2018-02-18 13:41:06 +00:00
"strings"
2018-02-21 23:17:07 +00:00
"time"
2018-02-10 13:37:23 +00:00
)
2022-02-03 17:05:17 +00:00
var (
2022-02-19 10:52:09 +00:00
loginSalt string = "adelina"
2022-02-03 17:05:17 +00:00
justLogin bool
)
2018-02-18 16:37:16 +00:00
2018-02-18 13:41:06 +00:00
type loginChecker struct {
2020-02-20 23:18:56 +00:00
students [ ] Student
authMethod AuthMethod
2018-02-10 13:37:23 +00:00
}
type loginUpload struct {
Username string
Password string
2023-02-21 14:24:04 +00:00
Force * bool
2018-02-10 13:37:23 +00:00
}
func ( l loginChecker ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
if addr := r . Header . Get ( "X-Forwarded-For" ) ; addr != "" {
r . RemoteAddr = addr
}
log . Printf ( "%s \"%s %s\" [%s]\n" , r . RemoteAddr , r . Method , r . URL . Path , r . UserAgent ( ) )
w . Header ( ) . Set ( "Content-Type" , "text/plain" )
// Check request type and size
if r . Method != "POST" {
http . Error ( w ,
"Invalid request" ,
http . StatusBadRequest )
return
} else if r . ContentLength < 0 || r . ContentLength > 1023 {
http . Error ( w ,
"Request entity too large" ,
http . StatusRequestEntityTooLarge )
return
}
dec := json . NewDecoder ( r . Body )
var lu loginUpload
if err := dec . Decode ( & lu ) ; err != nil {
http . Error ( w ,
err . Error ( ) ,
http . StatusBadRequest )
return
}
// Perform login check
canContinue := false
for _ , std := range l . students {
if std . Login == lu . Username {
canContinue = true
}
}
if ! canContinue {
2018-02-12 10:39:44 +00:00
log . Println ( "Login not found:" , lu . Username , "at" , r . RemoteAddr )
2018-02-10 13:37:23 +00:00
http . Error ( w , "Login not found in whitelist." , http . StatusUnauthorized )
return
}
2020-02-20 23:18:56 +00:00
if ok , err := l . authMethod . checkAuth ( lu . Username , lu . Password ) ; err != nil {
log . Println ( "Unable to perform authentication for" , lu . Username , ":" , err , "at" , r . RemoteAddr )
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return
} else if ! ok {
log . Println ( "Login failed:" , lu . Username , "at" , r . RemoteAddr )
http . Error ( w , "Invalid password" , http . StatusUnauthorized )
return
2018-02-18 13:41:06 +00:00
}
2022-02-03 17:05:17 +00:00
if justLogin {
log . Println ( "Successful login of" , lu . Username , "at" , r . RemoteAddr )
http . Error ( w , "You're now successfully logged." , http . StatusOK )
return
}
2018-03-05 16:36:25 +00:00
// Find corresponding MAC
2019-02-26 11:34:31 +00:00
var ip net . IP
2019-02-22 00:55:02 +00:00
spl := strings . SplitN ( r . RemoteAddr , ":" , 2 )
2019-02-26 11:34:31 +00:00
if ip = net . ParseIP ( spl [ 0 ] ) ; ip == nil {
2021-02-13 17:34:44 +00:00
http . Error ( w , "Unable to parse given IPv4: " + spl [ 0 ] , http . StatusInternalServerError )
2019-02-22 00:55:02 +00:00
return
2019-02-26 11:34:31 +00:00
}
var mac * ARPEntry
if tab , err := ARPAnalyze ( ) ; err != nil {
log . Println ( "Error on ARPAnalyze:" , err )
http . Error ( w , "Internal server error. Please retry in a few minutes" , http . StatusInternalServerError )
2019-02-22 00:55:02 +00:00
return
2018-03-05 16:36:25 +00:00
} else {
2019-02-26 11:34:31 +00:00
mac = ARPContainsIP ( tab , ip )
}
if mac == nil {
log . Printf ( "Unable to find MAC address for given IP (%s)\n" , ip )
2023-02-21 14:24:04 +00:00
http . Error ( w , "Unable to find the MAC address of your host. Please retry in a few minutes" , http . StatusInternalServerError )
2019-02-28 01:27:33 +00:00
return
2018-03-05 16:36:25 +00:00
}
2023-02-21 14:24:04 +00:00
// Check if the user is already registered
if exists , user , err := l . findUser ( lu . Username ) ; exists && ( user == nil || mac . HWAddress . String ( ) != user . MAC ) {
log . Printf ( "Find the user in DB: %v" , user )
if lu . Force == nil {
log . Println ( "Successful login of" , lu . Username , "at" , r . RemoteAddr )
http . Error ( w , "You are already registered on a different machine. If you continue, the other machine will no longer be able to use its dedicated IP." , http . StatusPaymentRequired )
return
} else if ! * lu . Force {
log . Println ( "Successful login of" , lu . Username , "at" , r . RemoteAddr )
http . Error ( w , fmt . Sprintf ( "Use the following IP: %s" , ip ) , http . StatusOK )
return
}
} else if err != nil {
log . Println ( "An error occurs when searching the user in current DB:" , err . Error ( ) )
}
2018-03-05 16:36:25 +00:00
// Register the user remotely
2019-02-26 22:48:01 +00:00
if ip , err := l . registerUser ( lu . Username , r . RemoteAddr , * mac ) ; err != nil {
2020-02-21 00:08:15 +00:00
log . Println ( "Error on remote registration for" , lu . Username , ":" , err )
2018-02-21 23:17:07 +00:00
http . Error ( w , "Internal server error. Please retry in a few minutes" , http . StatusInternalServerError )
return
2019-02-28 01:27:33 +00:00
} else if err := l . lateLoginAction ( lu . Username , r . RemoteAddr , * mac , ip ) ; err != nil {
2020-02-21 00:08:15 +00:00
log . Println ( "Error on late login action for" , lu . Username , ":" , err )
2018-02-12 10:39:44 +00:00
http . Error ( w , "Internal server error. Please retry in a few minutes" , http . StatusInternalServerError )
return
2019-02-26 22:48:01 +00:00
} else {
log . Println ( "Successful login of" , lu . Username , "at" , r . RemoteAddr )
http . Error ( w , fmt . Sprintf ( "Use the following IP: %s" , ip ) , http . StatusOK )
2018-02-12 10:39:44 +00:00
}
2018-02-10 13:37:23 +00:00
}
2018-02-12 10:39:44 +00:00
2019-02-28 01:27:33 +00:00
type myIP struct {
2021-02-13 17:34:44 +00:00
Id int64 ` json:"id" `
Login string ` json:"login" `
IP string ` json:"ip" `
2023-02-21 14:24:04 +00:00
MAC string ` json:"mac" `
}
func ( l loginChecker ) findUser ( username string ) ( bool , * myIP , error ) {
req , err := http . NewRequest ( "GET" , "https://adlin.nemunai.re/api/students/" + username + "/" , nil )
if err != nil {
return false , nil , err
}
resp , err := http . DefaultClient . Do ( req )
if err != nil {
return false , nil , err
}
defer resp . Body . Close ( )
if resp . StatusCode == http . StatusNotFound {
return false , nil , nil
} else if resp . StatusCode != http . StatusOK {
return false , nil , errors . New ( resp . Status )
} else {
dec := json . NewDecoder ( resp . Body )
var myip myIP
if err := dec . Decode ( & myip ) ; err != nil {
return true , nil , err
}
return true , & myip , nil
}
2019-02-28 01:27:33 +00:00
}
func ( l loginChecker ) registerUser ( username , remoteAddr string , ent ARPEntry ) ( net . IP , error ) {
2023-02-21 14:24:04 +00:00
bts , err := json . Marshal ( myIP { Login : username , IP : remoteAddr , MAC : ent . HWAddress . String ( ) } )
2018-02-21 23:17:07 +00:00
if err != nil {
2019-02-26 22:48:01 +00:00
return nil , nil
2018-02-21 23:17:07 +00:00
}
req , err := http . NewRequest ( "POST" , "https://adlin.nemunai.re/api/students/" , bytes . NewReader ( bts ) )
if err != nil {
2019-02-26 22:48:01 +00:00
return nil , err
2018-02-21 23:17:07 +00:00
}
2022-02-19 14:30:26 +00:00
h := hmac . New ( sha512 . New , [ ] byte ( loginSalt ) )
h . Write ( [ ] byte ( fmt . Sprintf ( "%d" , time . Now ( ) . Unix ( ) / 10 ) ) )
req . Header . Add ( "X-ADLIN-Authentication" , base64 . StdEncoding . EncodeToString ( h . Sum ( nil ) ) )
2018-02-21 23:17:07 +00:00
req . Header . Set ( "Content-Type" , "application/json" )
client := & http . Client { }
resp , err := client . Do ( req )
if err != nil {
2019-02-26 22:48:01 +00:00
return nil , err
2018-02-21 23:17:07 +00:00
}
2019-02-26 22:48:01 +00:00
defer resp . Body . Close ( )
2018-02-21 23:17:07 +00:00
if resp . StatusCode != http . StatusOK {
2019-02-26 22:48:01 +00:00
return nil , errors . New ( resp . Status )
2018-02-21 23:17:07 +00:00
} else {
2019-02-28 01:27:33 +00:00
dec := json . NewDecoder ( resp . Body )
var myip myIP
if err := dec . Decode ( & myip ) ; err != nil {
return nil , err
}
return net . ParseIP ( myip . IP ) , nil
2018-02-21 23:17:07 +00:00
}
}
2019-02-28 01:27:33 +00:00
func ( l loginChecker ) lateLoginAction ( username , remoteAddr string , mac ARPEntry , ip net . IP ) error {
2022-02-26 21:21:31 +00:00
return RegisterUserMAC ( mac , ip , username , remoteAddr )
2018-02-12 10:39:44 +00:00
}