replace zxq.co/ripple/hanayo

This commit is contained in:
Alicia
2019-02-23 13:29:15 +00:00
commit c3d206c173
5871 changed files with 1353715 additions and 0 deletions

View 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
View 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)
}

View 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
}
}
}