update login redirect (#1455)

try update callback api

Signed-off-by: zzjin <tczzjin@gmail.com>
This commit is contained in:
zzjin
2022-08-03 22:43:52 +08:00
committed by GitHub
parent 9c82480ac1
commit 2f3f95bcf6
27 changed files with 259 additions and 148 deletions
+3
View File
@@ -0,0 +1,3 @@
PUBLIC_URL=.
NEXT_PUBLIC_API_HOST=/
+38
View File
@@ -0,0 +1,38 @@
// .prettierrc.js
module.exports = {
// 一行最多 100 字符
printWidth: 100,
// 使用 4 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾不需要逗号
trailingComma: 'none',
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 换行符使用 lf
endOfLine: 'lf'
};
@@ -19,7 +19,6 @@ metadata:
namespace: sealos
---
# kind: ClusterRoleBinding
# apiVersion: rbac.authorization.k8s.io/v1
# metadata:
@@ -49,7 +48,6 @@ spec:
app: desktop-frontend
---
apiVersion: v1
kind: ConfigMap
metadata:
@@ -60,7 +58,6 @@ data:
addr: :3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -83,9 +80,9 @@ spec:
image: fanux/frontend:latest
imagePullPolicy: Always
volumeMounts:
- name: desktop-frontend-volume
mountPath: /auth.yaml
subPath: auth.yaml
- name: desktop-frontend-volume
mountPath: /auth.yaml
subPath: auth.yaml
volumes:
- name: desktop-frontend-volume
configMap:
+5 -1
View File
@@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
// @ts-check
/**
* @type {import('next').NextConfig}
**/
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
-13
View File
@@ -1,13 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
-43
View File
@@ -1,43 +0,0 @@
import type { NextPage } from 'next'
import Head from 'next/head'
import { UserOutlined, LockOutlined } from '@ant-design/icons'
import { Input, Button } from 'antd'
import styles from '../styles/Login.module.css'
import { useRouter } from 'next/router'
const Login: NextPage = () => {
const router = useRouter()
const handleLogin = () => {
router.push('/dashboard')
}
return (
<div className={styles.container}>
<Head>
<title>Login Page</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h2 className={styles.title}>
Sealos Login
</h2>
<div className={styles.form}>
<Input size="large" placeholder="please input your account" prefix={<UserOutlined />} />
<br />
<Input size="large" placeholder="please input your password" prefix={<LockOutlined />} />
<br />
<Button type='primary' onClick={handleLogin}>Submit</Button>
</div>
</main>
</div>
)
}
export default Login
+26
View File
@@ -0,0 +1,26 @@
const api_host = process.env.NEXT_PUBLIC_API_HOST;
export default async function fetchAPI(
query: string = '',
variables?: any,
method: string = 'GET'
) {
const headers = { 'Content-Type': 'application/json' };
if (method === 'GET' && variables !== undefined) {
query += '?' + new URLSearchParams(variables).toString();
}
const res = await fetch(api_host + query, {
headers,
method: method,
body: method !== 'GET' && variables ? JSON.stringify(variables) : undefined
});
const json = await res.json();
if (json.errors) {
console.error(json.errors);
throw new Error('Failed to fetch API');
}
return json.data;
}
@@ -1,10 +1,9 @@
import type { NextPage } from 'next'
import Head from 'next/head'
import styles from '../../styles/Dashboard.module.css'
import styles from './dashboard.module.css'
import { SearchOutlined } from '@ant-design/icons';
import { Tooltip, Button } from 'antd'
const Login: NextPage = () => {
return (
<div className={styles.container}>
@@ -18,16 +17,16 @@ const Login: NextPage = () => {
<div className={styles.menu}> header menu</div>
<div className={styles.container}>
<iframe className={styles.iframe} src="https://sealos.io/" title="baidu" />
<iframe className={styles.iframe} src="https://sealos.io/" title="sealos.io" />
</div>
<div className={styles.dock}>
<Tooltip title="search">
<Button type="primary" shape="circle" icon={<SearchOutlined />} />
</Tooltip>
<Tooltip title="search">
<Button type="primary" shape="circle" icon={<SearchOutlined />} />
</Tooltip>
<Tooltip title="search">
<Button type="primary" shape="circle" icon={<SearchOutlined />} />
</Tooltip>
<Tooltip title="search">
<Button type="primary" shape="circle" icon={<SearchOutlined />} />
</Tooltip>
</div>
</main>
</div>
@@ -44,7 +44,7 @@ const Home: NextPage = () => {
</a>
</Link>
<Link href="/login">
<Link href="/api/login">
<a className={styles.card}>
<h2>Login & Register &rarr;</h2>
<p>
@@ -0,0 +1,34 @@
import type { NextPage } from 'next'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import fetchAPI from '../../lib/api'
const Callback: NextPage = () => {
const [conf, setConf] = useState('')
const [redirect, setRedirect] = useState('')
const router = useRouter();
useEffect(() => {
if (!router.isReady) return;
const { code, state } = router.query;
if (code !== undefined && code !== '' && state !== undefined && state != '') {
fetchAPI('api/token', { code: code, state: state }).then((res) => {
console.log(res)
setConf(res)
if (res.access_token !== '') {
setRedirect('/dashboard')
}
}).catch((err) => {
console.log(err);
});
}
}, [router.isReady]);
return <>conf: {conf}<Link href={redirect}>dashboard</Link></>
}
export default Callback
@@ -0,0 +1,17 @@
import type { GetServerSideProps, NextPage } from 'next'
import { isUserLogin } from '../../store/session'
export const getServerSideProps: GetServerSideProps = async (context) => {
const is_login = isUserLogin();
let destination = '/api/login';
if (is_login) {
destination = '/dashboard';
}
return {
redirect: { statusCode: 302, destination: destination }
}
}
const Login: NextPage = () => <></>
export default Login
+34
View File
@@ -0,0 +1,34 @@
type Session = {
readonly token: string;
readonly uid: string;
readonly user_name: string;
};
let session: Session | undefined;
export function setUserInfo(ss: Session) {
localStorage.setItem('user_info', JSON.stringify(ss));
session = ss;
}
export function delUserInfo() {
localStorage.removeItem('user_info');
session = undefined;
}
export function getUserInfo() {
if (session === undefined) {
const got = localStorage.getItem('user_info');
if (got && got !== '') {
const got_obj = JSON.parse(got);
if (got_obj && got_obj.token !== '') {
session = got_obj;
}
}
}
return session;
}
export function isUserLogin() {
return session === undefined ? false : true;
}
-40
View File
@@ -1,40 +0,0 @@
.container {
padding: 0 2rem;
}
.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 400px;
margin: 0 auto;
}
.form {
width: 100%;
padding: 15px;
display: flex;
flex-direction: column;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
line-height: 1.15;
font-size: 2rem;;
margin-bottom: 40px;
}
+3 -2
View File
@@ -15,7 +15,7 @@ require (
github.com/docker/cli v20.10.17+incompatible
github.com/docker/docker v20.10.17+incompatible
github.com/docker/go-units v0.4.0
github.com/emicklei/go-restful v2.15.0+incompatible
github.com/emicklei/go-restful/v3 v3.9.0
github.com/emirpasic/gods v1.18.1
github.com/hashicorp/go-multierror v1.1.1
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.0.72
@@ -36,6 +36,7 @@ require (
github.com/spf13/pflag v1.0.5
go.uber.org/zap v1.21.0
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
@@ -86,6 +87,7 @@ require (
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/emicklei/go-restful v2.15.0+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/fsouza/go-dockerclient v1.7.11 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
@@ -170,7 +172,6 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect
+2
View File
@@ -422,6 +422,8 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4=
github.com/emicklei/go-restful v2.15.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+11 -2
View File
@@ -19,6 +19,7 @@ import (
"github.com/labring/sealos/pkg/auth/sso"
"github.com/labring/sealos/pkg/auth/utils"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)
var (
@@ -41,8 +42,16 @@ func GetLoginRedirect() (string, error) {
return redirectURL, errors.Wrap(err, "Get redirect url failed")
}
func GetKubeConfig(state, code string) (string, error) {
user, err := ssoClient.GetUserInfo(state, code)
func GetOAuthToken(state, code string) (*oauth2.Token, error) {
return ssoClient.GetToken(state, code)
}
func GetUserInfo(accessToken string) (*sso.User, error) {
return ssoClient.GetUserInfo(accessToken)
}
func GetKubeConfig(accessToken string) (string, error) {
user, err := ssoClient.GetUserInfo(accessToken)
if err != nil {
return "", errors.Wrap(err, "Get user info failed")
}
+17 -17
View File
@@ -18,17 +18,16 @@ import (
"context"
"encoding/json"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/pkg/errors"
casdoorAuth "github.com/casdoor/casdoor-go-sdk/auth"
"github.com/pkg/errors"
"golang.org/x/oauth2"
v1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/labring/sealos/pkg/auth/conf"
"github.com/labring/sealos/pkg/auth/utils"
"github.com/labring/sealos/pkg/client-go/kubernetes"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const name = "casdoor"
@@ -133,18 +132,19 @@ func (c *CasdoorClient) GetRedirectURL() (string, error) {
return casdoorAuth.GetSigninUrl(c.CallbackURL), nil
}
func (c *CasdoorClient) GetUserInfo(state, code string) (User, error) {
token, err := casdoorAuth.GetOAuthToken(code, state)
func (c *CasdoorClient) GetToken(state, code string) (*oauth2.Token, error) {
return casdoorAuth.GetOAuthToken(code, state)
}
func (c *CasdoorClient) GetUserInfo(accessToken string) (*User, error) {
casdoorUser, err := casdoorAuth.ParseJwtToken(accessToken)
if err != nil {
return User{}, errors.Wrap(err, "Get OAuth token failed")
return nil, errors.Wrap(err, "Parse Jwt token failed")
}
casdoorUser, err := casdoorAuth.ParseJwtToken(token.AccessToken)
if err != nil {
return User{}, errors.Wrap(err, "Parse Jwt token failed")
}
return User{
ID: casdoorUser.Id,
Name: casdoorUser.Name,
return &User{
ID: casdoorUser.Id,
Name: casdoorUser.Name,
Avatar: casdoorUser.Avatar,
}, nil
}
+7 -3
View File
@@ -19,16 +19,20 @@ import (
"github.com/labring/sealos/pkg/auth/conf"
"github.com/labring/sealos/pkg/utils/logger"
"golang.org/x/oauth2"
)
type User struct {
ID string
Name string
ID string `json:"id"`
Name string `json:"name"`
Avatar string `json:"avatar"`
}
type Client interface {
GetRedirectURL() (string, error)
GetUserInfo(state, code string) (User, error)
GetToken(state, code string) (*oauth2.Token, error)
GetUserInfo(accessToken string) (*User, error)
}
type ClientType string
+3 -3
View File
@@ -14,13 +14,13 @@
package httpserver
import "github.com/emicklei/go-restful"
import restful "github.com/emicklei/go-restful/v3"
//Cors is cors for restful utils
// Cors is cors for restful utils
func Cors(wsContainer *restful.Container) {
// Add container filter to enable CORS
cors := restful.CrossOriginResourceSharing{
ExposeHeaders: []string{"X-My-Header"},
// ExposeHeaders: []string{"X-My-Header"},
AllowedHeaders: []string{"Content-Type", "Accept"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
CookiesAllowed: true,
+2 -2
View File
@@ -17,11 +17,11 @@ package httpserver
import (
"net/http"
"github.com/emicklei/go-restful"
restful "github.com/emicklei/go-restful/v3"
"github.com/labring/sealos/pkg/utils/logger"
)
// GoRestful is a http server using go-restful
// GoRestful is a http server using go-restful/v3
func GoRestful(registerFunc func(*restful.WebService), addr string) error {
container := restful.NewContainer()
container.Router(restful.CurlyRouter{})
+41 -5
View File
@@ -15,7 +15,7 @@
package api
import (
"github.com/emicklei/go-restful"
restful "github.com/emicklei/go-restful/v3"
"github.com/labring/sealos/pkg/auth"
)
@@ -26,8 +26,12 @@ func RegisterRouter(webService *restful.WebService) {
Produces(restful.MIME_JSON)
// redirect to login page
webService.Route(webService.GET("/login").To(handlerLogin))
// SSO callback, generate kubeconfig according to user info
webService.Route(webService.GET("/config").To(handlerConfig))
// SSO callback, get token
webService.Route(webService.GET("/token").To(handlerToken))
// return user info
webService.Route(webService.GET("/userinfo").To(handlerUserInfo))
// generate kubeconfig according to user info
webService.Route(webService.GET("/kubeconfig").To(handlerKubeConfig))
}
func handlerLogin(_ *restful.Request, response *restful.Response) {
@@ -40,10 +44,42 @@ func handlerLogin(_ *restful.Request, response *restful.Response) {
response.WriteHeader(302)
}
func handlerConfig(request *restful.Request, response *restful.Response) {
func handlerToken(request *restful.Request, response *restful.Response) {
state := request.QueryParameter("state")
code := request.QueryParameter("code")
kubeConfig, err := auth.GetKubeConfig(state, code)
if state == "" || code == "" {
_ = response.WriteError(500, nil)
return
}
oauthToken, err := auth.GetOAuthToken(state, code)
if err != nil {
_ = response.WriteError(500, err)
return
}
_ = response.WriteEntity(oauthToken)
}
func handlerUserInfo(request *restful.Request, response *restful.Response) {
accessToken := request.QueryParameter("access_token")
userInfo, err := auth.GetUserInfo(accessToken)
if err != nil {
_ = response.WriteError(500, err)
return
}
_ = response.WriteEntity(userInfo)
}
func handlerKubeConfig(request *restful.Request, response *restful.Response) {
accessToken := request.QueryParameter("access_token")
if accessToken == "" {
_ = response.WriteError(500, nil)
return
}
kubeConfig, err := auth.GetKubeConfig(accessToken)
if err != nil {
_ = response.WriteError(500, err)
return
+2 -1
View File
@@ -2,13 +2,14 @@ module github.com/labring/sealos/service/auth
go 1.18
require github.com/emicklei/go-restful v2.15.0+incompatible
require github.com/emicklei/go-restful/v3 v3.9.0
require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/casdoor/casdoor-go-sdk v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful v2.15.0+incompatible // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
+2
View File
@@ -75,6 +75,8 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4=
github.com/emicklei/go-restful v2.15.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=