logout button
This commit is contained in:
commit
bd48b02ab1
|
@ -7,7 +7,7 @@ const { StaticDatabases } = require("./db/utils")
|
|||
const { jwt, local, google, authenticated } = require("./middleware")
|
||||
const { Cookies, UserStatus } = require("./constants")
|
||||
const { hash, compare } = require("./hashing")
|
||||
const { getAppId, setCookie, getCookie } = require("./utils")
|
||||
const { getAppId, setCookie, getCookie, clearCookie } = require("./utils")
|
||||
const {
|
||||
generateUserID,
|
||||
getUserParams,
|
||||
|
@ -46,5 +46,6 @@ module.exports = {
|
|||
getAppId,
|
||||
setCookie,
|
||||
getCookie,
|
||||
clearCookie,
|
||||
authenticated,
|
||||
}
|
||||
|
|
|
@ -2,10 +2,7 @@ import { cloneDeep } from "lodash/fp"
|
|||
import { get } from "svelte/store"
|
||||
import { findComponent, findComponentPath } from "./storeUtils"
|
||||
import { store } from "builderStore"
|
||||
import {
|
||||
tables as tablesStore,
|
||||
queries as queriesStores,
|
||||
} from "stores/backend"
|
||||
import { tables as tablesStore, queries as queriesStores } from "stores/backend"
|
||||
import { makePropSafe } from "@budibase/string-templates"
|
||||
import { TableNames } from "../constants"
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<script>
|
||||
import { TextButton as Button, Modal } from "@budibase/bbui"
|
||||
import { auth } from "stores/backend"
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Button text on:click={auth.logout}>
|
||||
<i class="ri-logout-box-line" />
|
||||
<p>Logout</p>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div i {
|
||||
font-size: 26px;
|
||||
color: var(--grey-7);
|
||||
margin-left: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div p {
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--ink);
|
||||
font-weight: 400;
|
||||
margin: 0 0 0 12px;
|
||||
}
|
||||
</style>
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
{#if $datasources.list.length === 0}
|
||||
<i>Connect your first datasource to start building.</i>
|
||||
{:else}
|
||||
<i>Select a datasource to edit</i>
|
||||
{/if}
|
||||
{:else}<i>Select a datasource to edit</i>{/if}
|
||||
|
||||
<style>
|
||||
i {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { Home as Link } from "@budibase/bbui"
|
||||
import { Home as Link, Button } from "@budibase/bbui"
|
||||
import {
|
||||
AppsIcon,
|
||||
HostingIcon,
|
||||
|
@ -9,6 +9,7 @@
|
|||
} from "components/common/Icons"
|
||||
import LoginForm from "components/login/LoginForm.svelte"
|
||||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte"
|
||||
import LogoutButton from "components/start/LogoutButton.svelte"
|
||||
import Logo from "/assets/budibase-logo.svg"
|
||||
import { auth } from "stores/backend"
|
||||
|
||||
|
@ -44,6 +45,7 @@
|
|||
</div>
|
||||
<div class="nav-bottom">
|
||||
<BuilderSettingsButton />
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,7 +25,7 @@ export function createAuthStore() {
|
|||
return json
|
||||
},
|
||||
logout: async () => {
|
||||
const response = await api.post(`/api/auth/logout`)
|
||||
const response = await api.post(`/api/admin/auth/logout`)
|
||||
const json = await response.json()
|
||||
set({ user: null })
|
||||
},
|
||||
|
|
|
@ -32,12 +32,15 @@ module.exports = async (ctx, next) => {
|
|||
updateCookie = true
|
||||
appId = requestAppId
|
||||
roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||
} else if (requestAppId != null) {
|
||||
} else if (
|
||||
requestAppId != null &&
|
||||
(appCookie == null || requestAppId !== appCookie.appId)
|
||||
) {
|
||||
const globalUser = await getGlobalUsers(ctx, requestAppId, ctx.user.email)
|
||||
updateCookie = true
|
||||
appId = requestAppId
|
||||
roleId = globalUser.roles[requestAppId] || BUILTIN_ROLE_IDS.PUBLIC
|
||||
} else if (requestAppId == null && appCookie != null) {
|
||||
} else if (appCookie != null) {
|
||||
appId = appCookie.appId
|
||||
roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Authenticated middleware sets the correct APP auth type information when the user is not in the builder 1`] = `
|
||||
Object {
|
||||
"apiKey": "1234",
|
||||
"role": Role {
|
||||
"_id": "ADMIN",
|
||||
"inherits": "POWER",
|
||||
"name": "Admin",
|
||||
"permissionId": "admin",
|
||||
},
|
||||
"roleId": "ADMIN",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Authenticated middleware sets the correct BUILDER auth type information when the x-budibase-type header is not 'client' 1`] = `
|
||||
Object {
|
||||
"apiKey": "1234",
|
||||
"role": Role {
|
||||
"_id": "BUILDER",
|
||||
"name": "Builder",
|
||||
"permissionId": "admin",
|
||||
},
|
||||
"roleId": "BUILDER",
|
||||
}
|
||||
`;
|
|
@ -1,124 +0,0 @@
|
|||
const { AuthTypes } = require("../../constants")
|
||||
const authenticatedMiddleware = require("../authenticated")
|
||||
const jwt = require("jsonwebtoken")
|
||||
jest.mock("jsonwebtoken")
|
||||
|
||||
class TestConfiguration {
|
||||
constructor(middleware) {
|
||||
this.middleware = authenticatedMiddleware
|
||||
this.ctx = {
|
||||
config: {},
|
||||
auth: {},
|
||||
cookies: {
|
||||
set: jest.fn(),
|
||||
get: jest.fn(),
|
||||
},
|
||||
headers: {},
|
||||
params: {},
|
||||
path: "",
|
||||
request: {
|
||||
headers: {},
|
||||
},
|
||||
throw: jest.fn(),
|
||||
}
|
||||
this.next = jest.fn()
|
||||
}
|
||||
|
||||
setHeaders(headers) {
|
||||
this.ctx.headers = headers
|
||||
}
|
||||
|
||||
executeMiddleware() {
|
||||
return this.middleware(this.ctx, this.next)
|
||||
}
|
||||
|
||||
afterEach() {
|
||||
jest.resetAllMocks()
|
||||
}
|
||||
}
|
||||
|
||||
describe("Authenticated middleware", () => {
|
||||
let config
|
||||
|
||||
beforeEach(() => {
|
||||
config = new TestConfiguration()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
config.afterEach()
|
||||
})
|
||||
|
||||
it("calls next() when on the builder path", async () => {
|
||||
config.ctx.path = "/builder"
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("sets a new cookie when the current cookie does not match the app id from context", async () => {
|
||||
const appId = "app_123"
|
||||
config.setHeaders({
|
||||
"x-budibase-app-id": appId,
|
||||
})
|
||||
config.ctx.cookies.get.mockImplementation(() => "cookieAppId")
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.cookies.set).toHaveBeenCalledWith(
|
||||
"budibase:currentapp",
|
||||
appId,
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("sets the correct BUILDER auth type information when the x-budibase-type header is not 'client'", async () => {
|
||||
config.ctx.cookies.get.mockImplementation(() => "budibase:builder:local")
|
||||
jwt.verify.mockImplementationOnce(() => ({
|
||||
apiKey: "1234",
|
||||
roleId: "BUILDER",
|
||||
}))
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.auth.authenticated).toEqual(AuthTypes.BUILDER)
|
||||
expect(config.ctx.user).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("sets the correct APP auth type information when the user is not in the builder", async () => {
|
||||
config.setHeaders({
|
||||
"x-budibase-type": "client",
|
||||
})
|
||||
config.ctx.cookies.get.mockImplementation(() => `budibase:app:local`)
|
||||
jwt.verify.mockImplementationOnce(() => ({
|
||||
apiKey: "1234",
|
||||
roleId: "ADMIN",
|
||||
}))
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.auth.authenticated).toEqual(AuthTypes.APP)
|
||||
expect(config.ctx.user).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("marks the user as unauthenticated when a token cannot be determined from the users cookie", async () => {
|
||||
config.executeMiddleware()
|
||||
expect(config.ctx.auth.authenticated).toBe(false)
|
||||
expect(config.ctx.user.role).toEqual({
|
||||
_id: "PUBLIC",
|
||||
name: "Public",
|
||||
permissionId: "public",
|
||||
})
|
||||
})
|
||||
|
||||
it("clears the cookie when there is an error authenticating in the builder", async () => {
|
||||
config.ctx.cookies.get.mockImplementation(() => "budibase:builder:local")
|
||||
jwt.verify.mockImplementationOnce(() => {
|
||||
throw new Error()
|
||||
})
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.cookies.set).toBeCalledWith("budibase:builder:local")
|
||||
})
|
||||
})
|
|
@ -1,6 +1,5 @@
|
|||
const authorizedMiddleware = require("../authorized")
|
||||
const env = require("../../environment")
|
||||
const { AuthTypes } = require("../../constants")
|
||||
const { PermissionTypes, PermissionLevels } = require("../../utilities/security/permissions")
|
||||
jest.mock("../../environment", () => ({
|
||||
prod: false,
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
mockAuthWithNoCookie()
|
||||
mockWorker()
|
||||
|
||||
function mockWorker() {
|
||||
jest.mock("../../utilities/workerRequests", () => ({
|
||||
getGlobalUsers: () => {
|
||||
return {
|
||||
email: "test@test.com",
|
||||
roles: {
|
||||
"app_test": "BASIC",
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
function mockReset() {
|
||||
jest.resetModules()
|
||||
mockWorker()
|
||||
}
|
||||
|
||||
function mockAuthWithNoCookie() {
|
||||
jest.resetModules()
|
||||
mockWorker()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: jest.fn(),
|
||||
setCookie: jest.fn(),
|
||||
getCookie: jest.fn(),
|
||||
Cookies: {},
|
||||
}))
|
||||
}
|
||||
|
||||
function mockAuthWithCookie() {
|
||||
jest.resetModules()
|
||||
mockWorker()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: () => {
|
||||
return "app_test"
|
||||
},
|
||||
setCookie: jest.fn(),
|
||||
getCookie: () => ({ appId: "app_different", roleId: "PUBLIC" }),
|
||||
Cookies: {
|
||||
Auth: "auth",
|
||||
CurrentApp: "currentapp",
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
class TestConfiguration {
|
||||
constructor() {
|
||||
this.next = jest.fn()
|
||||
this.throw = jest.fn()
|
||||
|
||||
this.ctx = {
|
||||
next: this.next,
|
||||
throw: this.throw
|
||||
}
|
||||
}
|
||||
|
||||
setUser() {
|
||||
this.ctx.user = {
|
||||
email: "test@test.com",
|
||||
}
|
||||
}
|
||||
|
||||
executeMiddleware() {
|
||||
// import as late as possible for mocks
|
||||
const currentAppMiddleware = require("../currentapp")
|
||||
return currentAppMiddleware(this.ctx, this.next)
|
||||
}
|
||||
}
|
||||
|
||||
describe("Current app middleware", () => {
|
||||
let config
|
||||
|
||||
beforeEach(() => {
|
||||
config = new TestConfiguration()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("test having no cookies or app ID", () => {
|
||||
it("should be able to proceed with nothing setup", async () => {
|
||||
await config.executeMiddleware()
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("check get public for app when not logged in", () => {
|
||||
it("should be able to proceed with no login, but cookies configured", async () => {
|
||||
mockAuthWithCookie()
|
||||
await config.executeMiddleware()
|
||||
expect(config.ctx.roleId).toEqual("PUBLIC")
|
||||
expect(config.ctx.appId).toEqual("app_test")
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("check functionality when logged in", () => {
|
||||
async function checkExpected(setCookie) {
|
||||
config.setUser()
|
||||
await config.executeMiddleware()
|
||||
const cookieFn = require("@budibase/auth").setCookie
|
||||
if (setCookie) {
|
||||
expect(cookieFn).toHaveBeenCalled()
|
||||
} else {
|
||||
expect(cookieFn).not.toHaveBeenCalled()
|
||||
}
|
||||
expect(config.ctx.roleId).toEqual("BASIC")
|
||||
expect(config.ctx.appId).toEqual("app_test")
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
}
|
||||
|
||||
it("should be able to setup an app token when cookie not setup", async () => {
|
||||
mockAuthWithCookie()
|
||||
await checkExpected(true)
|
||||
})
|
||||
|
||||
it("should perform correct when no cookie exists", async () => {
|
||||
mockReset()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: () => {
|
||||
return "app_test"
|
||||
},
|
||||
setCookie: jest.fn(),
|
||||
getCookie: jest.fn(),
|
||||
Cookies: {},
|
||||
}))
|
||||
await checkExpected(true)
|
||||
})
|
||||
|
||||
it("lastly check what occurs when cookie doesn't need updated", async () => {
|
||||
mockReset()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: () => {
|
||||
return "app_test"
|
||||
},
|
||||
setCookie: jest.fn(),
|
||||
getCookie: () => ({ appId: "app_test", roleId: "BASIC" }),
|
||||
Cookies: {},
|
||||
}))
|
||||
await checkExpected(false)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
|
@ -1,4 +1,4 @@
|
|||
const { passport, Cookies } = require("@budibase/auth")
|
||||
const { passport, Cookies, clearCookie } = require("@budibase/auth")
|
||||
|
||||
exports.authenticate = async (ctx, next) => {
|
||||
return passport.authenticate("local", async (err, user) => {
|
||||
|
@ -27,6 +27,15 @@ exports.authenticate = async (ctx, next) => {
|
|||
})(ctx, next)
|
||||
}
|
||||
|
||||
exports.logout = async ctx => {
|
||||
clearCookie(Cookies.Auth)
|
||||
ctx.body = { success: true }
|
||||
}
|
||||
|
||||
exports.googleAuth = async (ctx, next) => {
|
||||
// return passport.authenticate("google")
|
||||
}
|
||||
|
||||
exports.googleAuth = async (ctx, next) => {
|
||||
// return passport.authenticate("google")
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ const router = Router()
|
|||
|
||||
router
|
||||
.post("/api/admin/auth", authController.authenticate)
|
||||
.post("/api/admin/auth/logout", authController.logout)
|
||||
.get("/api/auth/google", passport.authenticate("google"))
|
||||
.get(
|
||||
"/api/auth/google/callback",
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
const env = require("../environment")
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
const selfHostKey =
|
||||
ctx.request.headers["x-budibase-auth"] || ctx.request.body.selfHostKey
|
||||
if (!selfHostKey || env.SELF_HOST_KEY !== selfHostKey) {
|
||||
ctx.throw(401, "Request unauthorised")
|
||||
} else {
|
||||
await next()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue