[REV]API ingress auth

This commit is contained in:
崔斌 2017-12-07 09:56:08 +08:00
parent ffc1b575a8
commit dc053301dc
15 changed files with 556 additions and 34 deletions

View File

@ -1,6 +1,160 @@
{
"swagger": "2.0",
"paths": {
"/cloud/api/manager": {
"get": {
"description": "get api manager",
"consumes": [
"application/json",
"application/x-protobuf"
],
"produces": [
"application/json",
"application/xml"
],
"tags": [
"cloud"
],
"summary": "获取api管理",
"operationId": "GetAPIManager",
"responses": {
"default": {
"description": "统一返回格式",
"schema": {
"$ref": "#/responses/commandResponse"
}
}
}
},
"post": {
"description": "get api manager",
"consumes": [
"application/json",
"application/x-protobuf"
],
"produces": [
"application/json",
"application/xml"
],
"tags": [
"cloud"
],
"summary": "获取api管理",
"operationId": "addAPIManager",
"parameters": [
{
"name": "Body",
"in": "body",
"schema": {
"type": "object",
"required": [
"class_level",
"prefix"
],
"properties": {
"alias;size:64": {
"description": "别称\nin: body",
"type": "string",
"x-go-name": "Alias"
},
"class_level": {
"description": "api级别\nin: body",
"type": "string",
"x-go-name": "ClassLevel"
},
"prefix": {
"description": "uri前部\nin: body",
"type": "string",
"x-go-name": "Prefix"
},
"remark;size:64": {
"description": "补充信息\nin:body",
"type": "string",
"x-go-name": "Remark"
},
"uri;size:256": {
"description": "完整uri\nin: body",
"type": "string",
"x-go-name": "URI"
}
}
}
}
],
"responses": {
"default": {
"description": "统一返回格式",
"schema": {
"$ref": "#/responses/commandResponse"
}
}
}
},
"delete": {
"description": "delete api manager",
"consumes": [
"application/json",
"application/x-protobuf"
],
"produces": [
"application/json",
"application/xml"
],
"tags": [
"cloud"
],
"summary": "获取api管理",
"operationId": "deleteAPIManager",
"parameters": [
{
"name": "Body",
"in": "body",
"schema": {
"type": "object",
"required": [
"class_level",
"prefix"
],
"properties": {
"alias;size:64": {
"description": "别称\nin: body",
"type": "string",
"x-go-name": "Alias"
},
"class_level": {
"description": "api级别\nin: body",
"type": "string",
"x-go-name": "ClassLevel"
},
"prefix": {
"description": "uri前部\nin: body",
"type": "string",
"x-go-name": "Prefix"
},
"remark;size:64": {
"description": "补充信息\nin:body",
"type": "string",
"x-go-name": "Remark"
},
"uri;size:256": {
"description": "完整uri\nin: body",
"type": "string",
"x-go-name": "URI"
}
}
}
}
],
"responses": {
"default": {
"description": "统一返回格式",
"schema": {
"$ref": "#/responses/commandResponse"
}
}
}
}
},
"/cloud/auth": {
"post": {
"description": "get token",

View File

@ -28,5 +28,7 @@ func Routes() chi.Router {
r := chi.NewRouter()
r.Get("/show", controller.GetCloudRouterManager().Show)
r.Post("/auth", controller.GetCloudRouterManager().GetToken)
r.Get("/api/manager", controller.GetCloudRouterManager().GetAPIManager)
r.Post("/api/manager", controller.GetCloudRouterManager().DeleteAPIManager)
return r
}

View File

@ -78,3 +78,97 @@ func (c *CloudManager) GetToken(w http.ResponseWriter, r *http.Request) {
}
httputil.ReturnSuccess(r, w, ti)
}
//GetAPIManager GetAPIManager
// swagger:operation GET /cloud/api/manager cloud GetAPIManager
//
// 获取api管理
//
// get api manager
//
// ---
// consumes:
// - application/json
// - application/x-protobuf
//
// produces:
// - application/json
// - application/xml
//
// responses:
// default:
// schema:
// "$ref": "#/responses/commandResponse"
// description: 统一返回格式
func (c *CloudManager) GetAPIManager(w http.ResponseWriter, r *http.Request) {
apiMap := handler.GetTokenIdenHandler().GetAPIManager()
httputil.ReturnSuccess(r, w, apiMap)
}
//AddAPIManager AddAPIManager
// swagger:operation POST /cloud/api/manager cloud addAPIManager
//
// 获取api管理
//
// get api manager
//
// ---
// consumes:
// - application/json
// - application/x-protobuf
//
// produces:
// - application/json
// - application/xml
//
// responses:
// default:
// schema:
// "$ref": "#/responses/commandResponse"
// description: 统一返回格式
func (c *CloudManager) AddAPIManager(w http.ResponseWriter, r *http.Request) {
var am api_model.APIManager
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &am, nil); !ok {
return
}
err := handler.GetTokenIdenHandler().AddAPIManager(&am)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, nil)
}
//DeleteAPIManager DeleteAPIManager
// swagger:operation DELETE /cloud/api/manager cloud deleteAPIManager
//
// 获取api管理
//
// delete api manager
//
// ---
// consumes:
// - application/json
// - application/x-protobuf
//
// produces:
// - application/json
// - application/xml
//
// responses:
// default:
// schema:
// "$ref": "#/responses/commandResponse"
// description: 统一返回格式
func (c *CloudManager) DeleteAPIManager(w http.ResponseWriter, r *http.Request) {
var am api_model.APIManager
if ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &am, nil); !ok {
return
}
err := handler.GetTokenIdenHandler().DeleteAPIManager(&am)
if err != nil {
err.Handle(r, w)
return
}
httputil.ReturnSuccess(r, w, nil)
}

View File

@ -11,7 +11,7 @@ import (
httputil "github.com/goodrain/rainbond/pkg/util/http"
)
//Events GetLogs
//Event GetLogs
func (e *TenantStruct) Event(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /v2/tenants/{tenant_name}/event v2 get events
//

View File

@ -43,6 +43,7 @@ import (
//CloudAction cloud action struct
type CloudAction struct {
RegionTag string
APISSL bool
CAPath string
KeyPath string
}
@ -50,9 +51,10 @@ type CloudAction struct {
//CreateCloudManager get cloud manager
func CreateCloudManager(conf option.Config) (*CloudAction, error) {
return &CloudAction{
APISSL: conf.APISSL,
RegionTag: conf.RegionTag,
CAPath: conf.WebsocketCertFile,
KeyPath: conf.WebsocketKeyFile,
CAPath: conf.APICertFile,
KeyPath: conf.APIKeyFile,
}, nil
}
@ -76,6 +78,9 @@ func (c *CloudAction) TokenDispatcher(gt *api_model.GetUserToken) (*api_model.To
return ti, nil
CREATE:
token := c.createToken(gt)
if !c.APISSL {
return ti, nil
}
//TODO: ca, key
ca, key, err := c.CertDispatcher(gt)
if err != nil {

View File

@ -19,9 +19,13 @@
package handler
import (
"fmt"
"strings"
"time"
"github.com/goodrain/rainbond/cmd/api/option"
api_model "github.com/goodrain/rainbond/pkg/api/model"
"github.com/goodrain/rainbond/pkg/api/util"
"github.com/goodrain/rainbond/pkg/db"
dbmodel "github.com/goodrain/rainbond/pkg/db/model"
)
@ -40,6 +44,60 @@ func (t *TokenIdenAction) AddTokenIntoMap(rui *dbmodel.RegionUserInfo) {
m[rui.Token] = rui
}
//GetAPIManager GetAPIManager
func (t *TokenIdenAction) GetAPIManager() map[string][]*dbmodel.RegionAPIClass {
return GetDefaultSourceURI()
}
//AddAPIManager AddAPIManager
func (t *TokenIdenAction) AddAPIManager(am *api_model.APIManager) *util.APIHandleError {
m := GetDefaultSourceURI()
ra := &dbmodel.RegionAPIClass{
ClassLevel: am.Body.ClassLevel,
Prefix: am.Body.Prefix,
}
if sourceList, ok := m[am.Body.ClassLevel]; ok {
sourceList = append(sourceList, ra)
} else {
//支持新增类型
newL := []*dbmodel.RegionAPIClass{ra}
m[am.Body.ClassLevel] = newL
}
ra.URI = am.Body.URI
ra.Alias = am.Body.Alias
ra.Remark = am.Body.Remark
if err := db.GetManager().RegionAPIClassDao().AddModel(ra); err != nil {
return util.CreateAPIHandleErrorFromDBError("add api manager", err)
}
return nil
}
//DeleteAPIManager DeleteAPIManager
func (t *TokenIdenAction) DeleteAPIManager(am *api_model.APIManager) *util.APIHandleError {
m := GetDefaultSourceURI()
if sourceList, ok := m[am.Body.ClassLevel]; ok {
var newL []*dbmodel.RegionAPIClass
for _, s := range sourceList {
if s.Prefix == am.Body.Prefix {
continue
}
newL = append(newL, s)
}
if len(newL) == 0 {
//该级别分组为空时,删除该资源分组
delete(m, am.Body.ClassLevel)
} else {
m[am.Body.ClassLevel] = newL
}
} else {
return util.CreateAPIHandleError(400, fmt.Errorf("have no api class level about %v", am.Body.ClassLevel))
}
if err := db.GetManager().RegionAPIClassDao().DeletePrefixInClass(am.Body.ClassLevel, am.Body.Prefix); err != nil {
return util.CreateAPIHandleErrorFromDBError("delete api prefix", err)
}
return nil
}
//CheckToken CheckToken
func (t *TokenIdenAction) CheckToken(token, uri string) bool {
m := GetDefaultTokenMap()
@ -51,20 +109,32 @@ func (t *TokenIdenAction) CheckToken(token, uri string) bool {
return false
}
switch regionInfo.APIRange {
case "source":
case dbmodel.ALLPOWER:
return true
case dbmodel.SERVERSOURCE:
sm := GetDefaultSourceURI()
if _, ok := sm[uri]; ok {
smL, ok := sm[dbmodel.SERVERSOURCE]
if !ok {
return false
}
return true
case "all":
return true
case "node":
sm := GetDefaultSourceURI()
if _, ok := sm[uri]; ok {
return true
for _, urinfo := range smL {
if strings.HasPrefix(uri, urinfo.Prefix) {
return true
}
return false
}
case dbmodel.NODEMANAGER:
sm := GetDefaultSourceURI()
smL, ok := sm[dbmodel.NODEMANAGER]
if !ok {
return false
}
for _, urinfo := range smL {
if strings.HasPrefix(uri, urinfo.Prefix) {
return true
}
return false
}
return false
}
return false
}

View File

@ -22,6 +22,9 @@ import (
"os"
"github.com/goodrain/rainbond/cmd/api/option"
api_model "github.com/goodrain/rainbond/pkg/api/model"
"github.com/goodrain/rainbond/pkg/api/util"
"github.com/goodrain/rainbond/pkg/db"
dbmodel "github.com/goodrain/rainbond/pkg/db/model"
)
@ -29,6 +32,9 @@ import (
type TokenMapHandler interface {
AddTokenIntoMap(rui *dbmodel.RegionUserInfo)
CheckToken(token, uri string) bool
GetAPIManager() map[string][]*dbmodel.RegionAPIClass
AddAPIManager(am *api_model.APIManager) *util.APIHandleError
DeleteAPIManager(am *api_model.APIManager) *util.APIHandleError
InitTokenMap() error
}
@ -39,7 +45,7 @@ type TokenMap map[string]*dbmodel.RegionUserInfo
var defaultTokenMap map[string]*dbmodel.RegionUserInfo
var defaultSourceURI map[string]int
var defaultSourceURI map[string][]*dbmodel.RegionAPIClass
//CreateTokenIdenHandler create token identification handler
func CreateTokenIdenHandler(conf option.Config) error {
@ -55,18 +61,32 @@ func CreateTokenIdenHandler(conf option.Config) error {
return nil
}
func createDefaultSourceURI() {
func createDefaultSourceURI() error {
if defaultSourceURI != nil {
return
return nil
}
SourceURI := make(map[string]int)
SourceURI["nodes"] = 1
SourceURI["tasks"] = 1
SourceURI["tasktemps"] = 1
SourceURI["taskgroups"] = 1
SourceURI["configs"] = 1
defaultSourceURI = SourceURI
return
var err error
defaultSourceURI, err = resourceURI()
if err != nil {
return err
}
return nil
}
func resourceURI() (map[string][]*dbmodel.RegionAPIClass, error) {
sourceMap := make(map[string][]*dbmodel.RegionAPIClass)
nodeSource, err := db.GetManager().RegionAPIClassDao().GetPrefixesByClass(dbmodel.NODEMANAGER)
if err != nil {
return nil, err
}
sourceMap[dbmodel.NODEMANAGER] = nodeSource
serverSource, err := db.GetManager().RegionAPIClassDao().GetPrefixesByClass(dbmodel.SERVERSOURCE)
if err != nil {
return nil, err
}
sourceMap[dbmodel.SERVERSOURCE] = serverSource
return sourceMap, nil
}
//CreateDefaultTokenMap CreateDefaultTokenMap
@ -101,6 +121,6 @@ func GetDefaultTokenMap() map[string]*dbmodel.RegionUserInfo {
}
//GetDefaultSourceURI GetDefaultSourceURI
func GetDefaultSourceURI() map[string]int {
func GetDefaultSourceURI() map[string][]*dbmodel.RegionAPIClass {
return defaultSourceURI
}

View File

@ -132,17 +132,10 @@ func FullToken(next http.Handler) http.Handler {
w.WriteHeader(http.StatusUnauthorized)
return
}
uris := strings.Split(r.RequestURI, "/")
if len(uris) < 3 {
util.CloseRequest(r)
w.WriteHeader(http.StatusBadRequest)
return
}
sourceURI := uris[2]
logrus.Debugf("request uri is %s", sourceURI)
logrus.Debugf("request uri is %s", r.RequestURI)
t := r.Header.Get("Authorization")
if tt := strings.Split(t, " "); len(tt) == 2 {
if handler.GetTokenIdenHandler().CheckToken(tt[1], sourceURI) {
if handler.GetTokenIdenHandler().CheckToken(tt[1], r.RequestURI) {
next.ServeHTTP(w, r)
return
}

View File

@ -50,3 +50,31 @@ type TokenInfo struct {
CA string `json:"ca"`
Key string `json:"key"`
}
//APIManager APIManager
//swagger:parameters addAPIManager deleteAPIManager
type APIManager struct {
//in: body
Body struct {
//api级别
//in: body
//required: true
ClassLevel string `json:"class_level" validate:"class_level|reqired"`
//uri前部
//in: body
//required: true
Prefix string `json:"prefix" validate:"prefix|required"`
//完整uri
//in: body
//required: false
URI string `json:"uri;size:256" validate:"uri"`
//别称
//in: body
//required: false
Alias string `json:"alias;size:64" validate:"alias"`
//补充信息
//in:body
//required: false
Remark string `json:"remark;size:64" validate:"remark"`
}
}

View File

@ -311,3 +311,10 @@ type RegionUserInfoDao interface {
GetALLTokenInValidityPeriod() ([]*model.RegionUserInfo, error)
GetTokenByEid(eid string) (*model.RegionUserInfo, error)
}
//RegionAPIClassDao RegionAPIClassDao
type RegionAPIClassDao interface {
Dao
GetPrefixesByClass(apiClass string) ([]*model.RegionAPIClass, error)
DeletePrefixInClass(apiClass, prefix string) error
}

View File

@ -92,6 +92,9 @@ type Manager interface {
RegionUserInfoDao() dao.RegionUserInfoDao
RegionUserInfoDaoTransactions(db *gorm.DB) dao.RegionUserInfoDao
RegionAPIClassDao() dao.RegionAPIClassDao
RegionAPIClassDaoTransactions(db *gorm.DB) dao.RegionAPIClassDao
}
var defaultManager Manager

43
pkg/db/model/apiClass.go Normal file
View File

@ -0,0 +1,43 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package model
//TableName 表名
func (t *RegionAPIClass) TableName() string {
return "region_api_class"
}
//RegionAPIClass RegionAPIClass
type RegionAPIClass struct {
Model
ClassLevel string `gorm:"column:class_level;size:24" json:"class_level"`
Prefix string `gorm:"column:prefix;size:128" json:"prefix"`
URI string `gorm:"column:uri;size:256" json:"uri"`
Alias string `gorm:"column:alias;size:64" json:"alias"`
Remark string `gorm:"column:remark;size:64" json:"remark"`
}
//NODEMANAGER NODEMANAGER
var NODEMANAGER = "node_manager"
//SERVERSOURCE SERVERSOURCE
var SERVERSOURCE = "server_source"
//ALLPOWER ALLPOWER
var ALLPOWER = "all_power"

View File

@ -0,0 +1,78 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dao
import (
"fmt"
"github.com/goodrain/rainbond/pkg/db/model"
"github.com/jinzhu/gorm"
)
//RegionAPIClassDaoImpl RegionAPIClassDaoImpl
type RegionAPIClassDaoImpl struct {
DB *gorm.DB
}
//AddModel 添加api分类信息
func (t *RegionAPIClassDaoImpl) AddModel(mo model.Interface) error {
info := mo.(*model.RegionAPIClass)
var oldInfo model.RegionAPIClass
if ok := t.DB.Where("prefix = ? and class_level=?", info.Prefix, info.ClassLevel).Find(&oldInfo).RecordNotFound(); ok {
if err := t.DB.Create(info).Error; err != nil {
return err
}
} else {
return fmt.Errorf("prefix %s is exist", info.Prefix)
}
return nil
}
//UpdateModel 更新api分类信息
func (t *RegionAPIClassDaoImpl) UpdateModel(mo model.Interface) error {
info := mo.(*model.RegionAPIClass)
if info.ID == 0 {
return fmt.Errorf("region user info id can not be empty when update ")
}
if err := t.DB.Save(info).Error; err != nil {
return err
}
return nil
}
//GetPrefixesByClass GetPrefixesByClass
func (t *RegionAPIClassDaoImpl) GetPrefixesByClass(apiClass string) ([]*model.RegionAPIClass, error) {
var racs []*model.RegionAPIClass
if err := t.DB.Select("prefix").Where("class_level =?", apiClass).Find(&racs).Error; err != nil {
return nil, err
}
return racs, nil
}
//DeletePrefixInClass DeletePrefixInClass
func (t *RegionAPIClassDaoImpl) DeletePrefixInClass(apiClass, prefix string) error {
relation := &model.RegionAPIClass{
ClassLevel: apiClass,
Prefix: prefix,
}
if err := t.DB.Where("class_level=? and prefix=?", apiClass, prefix).Delete(relation).Error; err != nil {
return err
}
return nil
}

View File

@ -425,3 +425,17 @@ func (m *Manager) RegionUserInfoDaoTransactions(db *gorm.DB) dao.RegionUserInfoD
DB: db,
}
}
//RegionAPIClassDao RegionAPIClassDao
func (m *Manager) RegionAPIClassDao() dao.RegionAPIClassDao {
return &mysqldao.RegionAPIClassDaoImpl{
DB: m.db,
}
}
//RegionAPIClassDaoTransactions RegionAPIClassDaoTransactions
func (m *Manager) RegionAPIClassDaoTransactions(db *gorm.DB) dao.RegionAPIClassDao {
return &mysqldao.RegionAPIClassDaoImpl{
DB: db,
}
}

View File

@ -130,6 +130,7 @@ func (m *Manager) RegisterTableModel() {
m.models = append(m.models, &model.VersionInfo{})
m.models = append(m.models, &model.TenantServicesStreamPluginPort{})
m.models = append(m.models, &model.RegionUserInfo{})
m.models = append(m.models, &model.RegionAPIClass{})
}
//CheckTable 检测表结构
@ -162,4 +163,14 @@ func (m *Manager) patchTable() {
// m.db.Exec("alter table tenant_services add replica_id varchar(32)")
// m.db.Exec("alter table tenant_services add status int(11) default 0")
// m.db.Exec("alter table tenant_services add node_label varchar(40)")
m.db.Exec("insert into region_api_class VALUES ('','','server_source','/v2/tenants','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','server_source','/v2/show','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','server_source','/v2/resources','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','server_source','/v2/opentsdb','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','node_manager','/v2/nodes','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','node_manager','/v2/job','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','node_manager','/v2/tasks','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','node_manager','/v2/taskgroups','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','node_manager','/v2/tasktemps','','','')")
m.db.Exec("insert into region_api_class VALUES ('','','node_manager','/v2/configs','','','')")
}