package api import ( "encoding/json" "fmt" "log" "net/http" authdiscord "example.com/auth/discord" configserver "example.com/config/server" function "example.com/database/function" functiontag "example.com/database/functiontag" group "example.com/database/group" user "example.com/database/user" "github.com/gin-gonic/gin" "github.com/xyproto/randomstring" "golang.org/x/oauth2" "gorm.io/gorm" ) var GlobalDatabase *gorm.DB var GlobalConfig configserver.AppConfig var GlobalOAuth *oauth2.Config // Private Functions // Authentication Workflow func AuthCallback(context *gin.Context) { oauthState := randomstring.CookieFriendlyString(32) context.Set("state", oauthState) var err error oauthToken, err := GlobalOAuth.Exchange(context.Request.Context(), context.Query("code")) if err != nil { context.Redirect(http.StatusInternalServerError, GlobalConfig.GetFrontendRootDomain()) } oauthTokenJSON, _ := json.Marshal(oauthToken) context.SetCookie("discord-oauthtoken", string(oauthTokenJSON), 0, "", GlobalConfig.API.Domain, false, false) context.Set("discord-oauthtoken", string(oauthTokenJSON)) CreateOrUpdateUser(context) context.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%sdashboard", GlobalConfig.GetFrontendRootDomain())) } func AuthLoginRedirect(context *gin.Context) { context.Redirect(302, GlobalOAuth.AuthCodeURL(context.GetString("state"))) } func AuthLogoutRedirect(context *gin.Context) { oauthTokenJSON, err := context.Cookie("discord-oauthtoken") if err == nil { user.Logout(GlobalDatabase, oauthTokenJSON) context.SetCookie("discord-oauthtoken", "", -1, "", GlobalConfig.API.Domain, false, true) } else { log.Println(err) } context.Redirect(http.StatusTemporaryRedirect, GlobalConfig.GetFrontendRootDomain()) } func CreateOrUpdateUser(context *gin.Context) { oauthTokenJSON := context.GetString("discord-oauthtoken") err := error(nil) if oauthTokenJSON == "" { oauthTokenJSON, err = context.Cookie("discord-oauthtoken") if err != nil { log.Printf("This really shouldn't happen. %s", err) context.AbortWithStatus(http.StatusInternalServerError) } } var oauthToken *oauth2.Token err = json.Unmarshal([]byte(oauthTokenJSON), &oauthToken) if err != nil { log.Println(err) context.AbortWithStatus(http.StatusBadRequest) } var currentDiscordUser authdiscord.DiscordUser currentDiscordUser.GetDiscordUser(context, oauthToken, GlobalConfig, GlobalOAuth) updatedDatabaseUser := user.User{ DisplayName: currentDiscordUser.Global_Name, Username: currentDiscordUser.Username, Id: currentDiscordUser.Id, Avatar: currentDiscordUser.Avatar, AvatarDecoration: currentDiscordUser.Avatar_Decoration_Data.Asset, LoginToken: string(oauthTokenJSON), LoggedIn: true, ApiKey: nil, } if user.Exists(GlobalDatabase, currentDiscordUser.Id) { dbOAuthToken := user.GetTokenFromUserId(GlobalDatabase, currentDiscordUser.Id) if user.GetLoggedInFromDiscordId(GlobalDatabase, currentDiscordUser.Id) { context.SetCookie("discord-oauthtoken", dbOAuthToken, 0, "", GlobalConfig.API.Domain, false, false) updatedDatabaseUser.LoginToken = string(dbOAuthToken) } else { context.SetCookie("discord-oauthtoken", string(oauthTokenJSON), 0, "", GlobalConfig.API.Domain, false, false) } err := updatedDatabaseUser.Update(GlobalDatabase) if err != nil { log.Println(err) context.AbortWithStatus(http.StatusInternalServerError) } } else { err := updatedDatabaseUser.Create(GlobalDatabase) if err != nil { log.Println(err) context.Copy().AbortWithStatus(http.StatusInternalServerError) } } } // Create Endpoints (post/) func CreateGroup(context *gin.Context) { GetUserLoggedIn(context) isAuthorized := context.GetBool("is-authorized") if isAuthorized { name, nameOk := context.GetQuery("name") if nameOk { group.Create(GlobalDatabase, name) context.Status(http.StatusOK) } else { context.AbortWithStatus(http.StatusBadRequest) } } else { context.AbortWithStatus(http.StatusUnauthorized) } } func CreateFunction(context *gin.Context) { GetUserLoggedIn(context) isAuthorized := context.GetBool("is-authorized") if isAuthorized { name, nameOk := context.GetQuery("name") tags := context.QueryArray("tags") requirements := context.QueryArray("requirements") if nameOk { function.Create(GlobalDatabase, name, tags, requirements) context.Status(http.StatusOK) } else { context.AbortWithStatus(http.StatusBadRequest) } } else { context.AbortWithStatus(http.StatusUnauthorized) } } func CreateFunctionTag(context *gin.Context) { GetUserLoggedIn(context) isAuthorized := context.GetBool("is-authorized") if isAuthorized { name := context.Query("name") if name != "" { functiontag.Create(GlobalDatabase, name) context.Status(http.StatusOK) } else { context.AbortWithStatus(http.StatusBadRequest) } } else { context.AbortWithStatus(http.StatusUnauthorized) } } // Update Endpoints (put/) // Read Endpoints (get/) func GetDiscordUser(context *gin.Context) { oauthTokenJSON, err := context.Cookie("discord-oauthtoken") if err == nil { var oauthToken *oauth2.Token err := json.Unmarshal([]byte(oauthTokenJSON), &oauthToken) if err == nil { if oauthToken.Valid() { var discordUser authdiscord.DiscordUser discordUser.GetDiscordUser(context, oauthToken, GlobalConfig, GlobalOAuth) if user.GetLoggedInFromDiscordId(GlobalDatabase, discordUser.Id) { context.JSON(http.StatusOK, discordUser) } else { context.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%slogout", GlobalConfig.GetAPIRootDomain())) } return } else { log.Println(err) context.AbortWithStatus(http.StatusBadRequest) } } else { log.Println(err) context.AbortWithStatus(http.StatusBadRequest) } } else { log.Println(err) context.AbortWithStatus(http.StatusBadRequest) } context.Redirect(http.StatusTemporaryRedirect, GlobalConfig.GetFrontendRootDomain()) } func GetUserLoggedIn(context *gin.Context) { oauthTokenJSON, err := context.Cookie("discord-oauthtoken") if err == nil { var oauthToken *oauth2.Token err := json.Unmarshal([]byte(oauthTokenJSON), &oauthToken) if err == nil { if oauthToken.Valid() { if user.GetLoggedInFromOAuthToken(GlobalDatabase, oauthTokenJSON) { context.JSON(http.StatusOK, gin.H{ "message": true, }) context.Set("is-authorized", true) return } } } } context.JSON(http.StatusUnauthorized, gin.H{ "message": false, }) context.Set("is-authorized", false) } func GetGroups(context *gin.Context) { groupNames, ok := context.GetQueryArray("groups") if ok { context.JSON(http.StatusOK, gin.H{ "groups": group.Get(GlobalDatabase, groupNames), }) } else { context.Status(http.StatusBadRequest) } } func GetAllGroups(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "groups": group.GetAll(GlobalDatabase), }) } func GetFunctions(context *gin.Context) { functionNames, ok := context.GetQueryArray("functions") if ok { context.JSON(http.StatusOK, gin.H{ "functions": function.Get(GlobalDatabase, functionNames), }) } else { context.Status(http.StatusBadRequest) } } func GetAllFunctions(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "functions": function.GetAll(GlobalDatabase), }) } func GetFunctionTags(context *gin.Context) { functionTagNames, ok := context.GetQueryArray("functiontags") if ok { context.JSON(http.StatusOK, gin.H{ "function_tags": functiontag.Get(GlobalDatabase, functionTagNames), }) } else { context.Status(http.StatusBadRequest) } } func GetAllFunctionTags(context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "function_tags": functiontag.GetAll(GlobalDatabase), }) } // Delete Endpoints (delete/)