replace zxq.co/ripple/hanayo
This commit is contained in:
33
routers/oauth/client_sha256.go
Normal file
33
routers/oauth/client_sha256.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/felipeweb/osin-mysql"
|
||||
)
|
||||
|
||||
// storage is a custom type of storage that implements client secrets saved in the dabase as sha256 hashes
|
||||
type storage struct {
|
||||
*mysql.Storage
|
||||
}
|
||||
|
||||
func (s storage) GetClient(id string) (osin.Client, error) {
|
||||
cl, err := s.Storage.GetClient(id)
|
||||
return client{cl}, err
|
||||
}
|
||||
|
||||
func (s storage) Clone() osin.Storage {
|
||||
return s
|
||||
}
|
||||
|
||||
type client struct {
|
||||
osin.Client
|
||||
}
|
||||
|
||||
func (c client) ClientSecretMatches(i string) bool {
|
||||
a := c.GetSecret() == fmt.Sprintf("%x", sha256.Sum256([]byte(i)))
|
||||
//fmt.Println("SES", a)
|
||||
return a
|
||||
}
|
171
routers/oauth/oauth.go
Normal file
171
routers/oauth/oauth.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/felipeweb/osin-mysql"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var osinServer *osin.Server
|
||||
|
||||
var rh RequestHandler
|
||||
|
||||
// Initialise initialises the oauth router.
|
||||
func Initialise(handler RequestHandler) error {
|
||||
store := mysql.New(handler.GetDB(), "osin_")
|
||||
|
||||
rh = handler
|
||||
|
||||
config := osin.NewServerConfig()
|
||||
config.AllowClientSecretInParams = true
|
||||
config.AllowGetAccessRequest = true
|
||||
// Hmm... Wondering why we're making access tokens everlasting, and
|
||||
// disabling refresh tokens? http://telegra.ph/On-refresh-tokens-06-10
|
||||
config.AccessExpiration = 0
|
||||
osinServer = osin.NewServer(config, storage{store})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Client is the data passed to the template of the authorization page.
|
||||
type Client struct {
|
||||
ID string
|
||||
Name string
|
||||
CreatorID int
|
||||
Avatar string
|
||||
Authorizations []string
|
||||
}
|
||||
|
||||
func clientFromOsinClient(oc osin.Client) Client {
|
||||
userDataRaw := oc.GetUserData().(string)
|
||||
|
||||
var userData [3]string
|
||||
json.Unmarshal([]byte(userDataRaw), &userData)
|
||||
|
||||
cid, _ := strconv.Atoi(userData[1])
|
||||
|
||||
return Client{
|
||||
ID: oc.GetId(),
|
||||
Name: userData[0],
|
||||
CreatorID: cid,
|
||||
Avatar: userData[2],
|
||||
// Authorizations is managed by caller
|
||||
}
|
||||
}
|
||||
|
||||
// RequestHandler contains the functions to which Oauth will delegate.
|
||||
type RequestHandler interface {
|
||||
// Checks whether the user is logged in. If they are, it returns true. Otherwise, it redirects
|
||||
// the user to the login page.
|
||||
CheckLoggedInOrRedirect(c *gin.Context) bool
|
||||
// DisplayAuthorizePage renders the page where the user can validate the request for authorization
|
||||
DisplayAuthorizePage(client Client, c *gin.Context)
|
||||
// CheckCSRF is a function that checks the POSTed parameter `csrf` is valid.
|
||||
CheckCSRF(c *gin.Context, s string) bool
|
||||
// GetDB retrieves MySQL's DB.
|
||||
GetDB() *sql.DB
|
||||
// GetUserID retrieves the ID of the currently logged in user.
|
||||
GetUserID(c *gin.Context) int
|
||||
}
|
||||
|
||||
// Authorize handles a request for user authorization
|
||||
func Authorize(c *gin.Context) {
|
||||
resp := osinServer.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
// first we let osinserver handle the authorize request
|
||||
if ar := osinServer.HandleAuthorizeRequest(resp, c.Request); ar != nil {
|
||||
// we then make sure to be logged in
|
||||
if !rh.CheckLoggedInOrRedirect(c) {
|
||||
return
|
||||
}
|
||||
|
||||
// and show the authorization page
|
||||
client := clientFromOsinClient(ar.Client)
|
||||
client.Authorizations = safeScopes(strings.Split(ar.Scope, " "))
|
||||
if c.PostForm("appid") != ar.Client.GetId() || !rh.CheckCSRF(c, c.PostForm("csrf")) {
|
||||
rh.DisplayAuthorizePage(client, c)
|
||||
return
|
||||
}
|
||||
|
||||
// all good, authorization succeded
|
||||
ar.Authorized = c.PostForm("approve") == "1"
|
||||
ar.UserData = strconv.Itoa(rh.GetUserID(c))
|
||||
ar.Scope = strings.Join(client.Authorizations, " ")
|
||||
osinServer.FinishAuthorizeRequest(resp, c.Request, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Println(resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, c.Writer, c.Request)
|
||||
}
|
||||
|
||||
// an "identify" scope is taken for granted
|
||||
var scopes = [...]string{
|
||||
"read_confidential",
|
||||
"write",
|
||||
}
|
||||
|
||||
// safeScopes removes duplicates and invalid elements from the raw scopes
|
||||
func safeScopes(rawScopes []string) []string {
|
||||
retScopes := make([]string, 0, len(scopes))
|
||||
for _, el := range scopes {
|
||||
for _, rawScope := range rawScopes {
|
||||
if rawScope == el {
|
||||
retScopes = append(retScopes, rawScope)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return retScopes
|
||||
}
|
||||
|
||||
// Token handles a request from a client to obtain an access token.
|
||||
func Token(c *gin.Context) {
|
||||
resp := osinServer.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
_ = resp.Storage.(storage)
|
||||
|
||||
c.Request.ParseForm()
|
||||
|
||||
if ar := osinServer.HandleAccessRequest(resp, c.Request); ar != nil {
|
||||
ar.Authorized = true
|
||||
ar.GenerateRefresh = false
|
||||
|
||||
redirectURI := c.Query("redirect_uri")
|
||||
// Get redirect uri from AccessRequest if it's there (e.g., refresh token request)
|
||||
if ar.RedirectUri != "" {
|
||||
redirectURI = ar.RedirectUri
|
||||
}
|
||||
|
||||
ar.ForceAccessData = &osin.AccessData{
|
||||
Client: ar.Client,
|
||||
AuthorizeData: ar.AuthorizeData,
|
||||
AccessData: ar.AccessData,
|
||||
RedirectUri: redirectURI,
|
||||
CreatedAt: osinServer.Now(),
|
||||
ExpiresIn: ar.Expiration,
|
||||
UserData: ar.UserData,
|
||||
Scope: ar.Scope,
|
||||
}
|
||||
|
||||
// generate access token
|
||||
plainToken, _, _ := osinServer.AccessTokenGen.GenerateAccessToken(ar.ForceAccessData, ar.GenerateRefresh)
|
||||
ar.ForceAccessData.AccessToken = fmt.Sprintf("%x", sha256.Sum256([]byte(plainToken)))
|
||||
|
||||
osinServer.FinishAccessRequest(resp, c.Request, ar)
|
||||
resp.Output["access_token"] = plainToken
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
delete(resp.Output, "expire_in")
|
||||
osin.OutputJSON(resp, c.Writer, c.Request)
|
||||
}
|
91
routers/pagemappings/pagemappings.go
Normal file
91
routers/pagemappings/pagemappings.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package pagemappings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var pageMappings = map[int]interface{}{
|
||||
1: "/",
|
||||
2: "/login",
|
||||
3: "/register",
|
||||
4: "/", // user cp (deleted)
|
||||
5: "/settings/avatar",
|
||||
6: "/settings",
|
||||
7: "/settings/password",
|
||||
8: "/settings/userpage",
|
||||
9: "/", // some deleted pages
|
||||
10: "/",
|
||||
11: "/",
|
||||
12: "/",
|
||||
13: "/leaderboard",
|
||||
14: "/doc",
|
||||
15: "/", // ripple v1 documentation stuff. this stuff is so old I won't even bother.
|
||||
16: func(u url.URL) string {
|
||||
return fmt.Sprintf("/doc/%s", u.Query().Get("id"))
|
||||
},
|
||||
17: "/changelog",
|
||||
18: "/pwreset",
|
||||
19: func(u url.URL) string {
|
||||
return fmt.Sprintf("/pwreset/continue?k=%s", u.Query().Get("k"))
|
||||
},
|
||||
20: "/", // Beta keys
|
||||
21: "/about",
|
||||
22: "/", // reports
|
||||
23: "/doc/rules", // rules
|
||||
24: "/", // my report
|
||||
25: "/", // report
|
||||
26: "/friends",
|
||||
27: "https://status.ripple.moe",
|
||||
28: "/", // user lookup
|
||||
29: "/2fa_gateway",
|
||||
30: "/settings/2fa",
|
||||
31: "/beatmaps/rank_request",
|
||||
32: "/dev/applications",
|
||||
33: "/dev/applications", // Theorically, this should be something like /dev/applications/<id>, but no-one ever used that page so who gives a fuck.
|
||||
34: "/donate",
|
||||
35: "/team",
|
||||
36: "/irc",
|
||||
37: "/beatmaps",
|
||||
38: "/register/verify",
|
||||
39: "/register/welcome",
|
||||
40: "/settings/discord",
|
||||
41: "/register", // elmo
|
||||
}
|
||||
|
||||
// CheckRedirect checks if the request is to be redirected to another page.
|
||||
// This is to avoid broken links because of the old website.
|
||||
func CheckRedirect(c *gin.Context) {
|
||||
p := c.Request.URL.Path
|
||||
if p != "/" && p != "/index.php" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
if c.Query("u") != "" {
|
||||
c.Redirect(302, "/u/"+c.Query("u"))
|
||||
c.Abort()
|
||||
return
|
||||
} else if i, _ := strconv.Atoi(c.Query("p")); i != 0 {
|
||||
mapped := pageMappings[i]
|
||||
if mapped == nil {
|
||||
u := c.Request.URL
|
||||
u.Host = "old.ripple.moe"
|
||||
c.Redirect(302, u.String())
|
||||
return
|
||||
}
|
||||
if str, ok := mapped.(string); ok {
|
||||
c.Redirect(302, str)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if f, ok := mapped.(func(url.URL) string); ok {
|
||||
c.Redirect(302, f(*c.Request.URL))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user