port full auth flow to cf workers

This commit is contained in:
41666 2020-12-05 03:09:20 -05:00
parent 9eeb946389
commit aad0987dce
50 changed files with 551 additions and 1167 deletions

View file

@ -1,40 +0,0 @@
package faas
import (
"errors"
"net/http"
)
type AuthLevel uint
const (
AuthGuest = AuthLevel(iota)
AuthUser
AuthAdmin
AuthSuper
)
var (
ErrNotAuthorized = errors.New("common/faas: session not authorized")
)
func assertAuthLevel(err error, requiredAuthLevel AuthLevel, assertedAuthLevel AuthLevel) error {
if requiredAuthLevel == assertedAuthLevel {
return nil
} else {
return err
}
}
// AuthMustMatch will assert the current session's authorization group/level; only can match for Guest, User, and Super.
func AuthMustMatch(request *http.Request, authLevel AuthLevel) error {
_, err := request.Cookie("Authorization")
if errors.Is(err, http.ErrNoCookie) {
// No cookie is present, assert guest.
return assertAuthLevel(ErrNotAuthorized, authLevel, AuthGuest)
} else if err != nil {
return err
}
return nil
}

View file

@ -1,75 +0,0 @@
package faas
import (
"html/template"
"net/http"
"time"
)
var bounceHTML = template.Must(template.New("bounceHTML").Parse(
`<!doctype html>
<meta charset="utf8">
<title>Redirecting...</title>
<meta http-equiv="refresh" content="0;URL='{{.Location}}'">
<style>
body {
background-color: #453E3D;
color: #AB9B9A;
}
a {
color: #AB9B9A;
}
</style>
<p>
Redirecting you to <a href="{{.Location}}">{{.Location}}</a>
</p>
`,
))
type bounceData struct {
Location string
}
// Bounce will do a 303 See Other response with url.
func Bounce(rw http.ResponseWriter, url string) {
rw.Header().Add("location", url)
rw.WriteHeader(303)
bounceHTML.Execute(rw, bounceData{Location: url})
}
// Stash will save the specified URL for later use in Unstash(), e.g. after an OAuth bounce
func Stash(rw http.ResponseWriter, url string) {
if url == "" {
return
}
cookie := http.Cookie{
Name: "rp_stashed_url",
Value: url,
HttpOnly: true,
Expires: time.Now().Add(5 * time.Minute),
}
rw.Header().Add("set-cookie", cookie.String())
}
// Unstash will redirect/Bounce() to a previously stashed URL or the defaultURL, whichever is available.
func Unstash(rw http.ResponseWriter, req *http.Request, defaultURL string) {
redirectURL := defaultURL
cookie, _ := req.Cookie("rp_stashed_url")
if cookie != nil && cookie.Expires.After(time.Now()) && cookie.Value != "" {
redirectURL = cookie.Value
}
unsetter := http.Cookie{
Name: "rp_stashed_url",
Value: "",
MaxAge: -1,
HttpOnly: true,
}
rw.Header().Set("set-cookie", unsetter.String())
Bounce(rw, redirectURL)
}

View file

@ -1,15 +0,0 @@
package faas
import (
"net/http"
"github.com/roleypoly/roleypoly/src/common/types"
)
func Fingerprint(req *http.Request) types.Fingerprint {
return types.Fingerprint{
UserAgent: req.UserAgent(),
ClientIP: req.RemoteAddr,
ForwardedFor: req.Header.Get("x-forwarded-for"),
}
}

View file

@ -34,3 +34,16 @@ export type PresentableGuild = {
export type GuildEnumeration = {
guildsList: PresentableGuild[];
};
export enum UserGuildPermissions {
User,
Manager,
Admin,
}
export type GuildSlug = {
id: string;
name: string;
icon: string;
permissionLevel: UserGuildPermissions;
};

View file

@ -0,0 +1,18 @@
import { GuildSlug } from './Guild';
import { DiscordUser } from './User';
export type AuthTokenResponse = {
access_token: string;
token_type: 'Bearer';
expires_in: number;
refresh_token: string;
scope: string;
};
export type SessionData = {
/** sessionID is a KSUID */
sessionID: string;
tokens: AuthTokenResponse;
user: DiscordUser;
guilds: GuildSlug[];
};

View file

@ -2,3 +2,4 @@ export * from './Role';
export * from './Category';
export * from './Guild';
export * from './User';
export * from './Session';

View file

@ -1,8 +1,12 @@
import { Role } from 'roleypoly/common/types';
import { Role } from '../types';
export const evaluatePermission = (haystack: number, needle: number): boolean => {
return (haystack & needle) === needle;
};
export const hasPermission = (roles: Role[], permission: number): boolean => {
const aggregateRoles = roles.reduce((acc, role) => acc | role.permissions, 0);
return (aggregateRoles & permission) === permission;
return evaluatePermission(aggregateRoles, permission);
};
export const hasPermissionOrAdmin = (roles: Role[], permission: number): boolean =>

View file

@ -0,0 +1 @@
export const isBrowser = () => typeof window !== 'undefined';