mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
Add API to ping OIDC endpoint
This commit adds an API to help admin verify the OIDC endpoint is a valid one. Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
parent
e0e6a1d30b
commit
96e2e0b145
@ -3478,6 +3478,39 @@ paths:
|
|||||||
description: The robot account is not found.
|
description: The robot account is not found.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
|
'/system/oidc/ping':
|
||||||
|
post:
|
||||||
|
summary: Test the OIDC endpoint.
|
||||||
|
description: Test the OIDC endpoint, the setting of the endpoint is provided in the request. This API can only
|
||||||
|
be called by system admin.
|
||||||
|
tags:
|
||||||
|
- Products
|
||||||
|
- System
|
||||||
|
parameters:
|
||||||
|
- name: endpoint
|
||||||
|
in: body
|
||||||
|
description: Request body for OIDC endpoint to be tested.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
url:
|
||||||
|
type: string
|
||||||
|
description: The URL of OIDC endpoint to be tested.
|
||||||
|
verify_cert:
|
||||||
|
type: boolean
|
||||||
|
description: Whether the certificate should be verified
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The specified robot account is successfully deleted.
|
||||||
|
'400':
|
||||||
|
description: The ping failed
|
||||||
|
'401':
|
||||||
|
description: User need to log in first.
|
||||||
|
'403':
|
||||||
|
description: User does not have permission to call this API
|
||||||
|
'500':
|
||||||
|
description: Unexpected internal errors.
|
||||||
'/system/CVEWhitelist':
|
'/system/CVEWhitelist':
|
||||||
get:
|
get:
|
||||||
summary: Get the system level whitelist of CVE.
|
summary: Get the system level whitelist of CVE.
|
||||||
|
@ -206,3 +206,19 @@ func RefreshToken(ctx context.Context, token *Token) (*Token, error) {
|
|||||||
}
|
}
|
||||||
return &Token{Token: *t, IDToken: it}, nil
|
return &Token{Token: *t, IDToken: it}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conn wraps connection info of an OIDC endpoint
|
||||||
|
type Conn struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
VerifyCert bool `json:"verify_cert"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEndpoint tests whether the endpoint is a valid OIDC endpoint.
|
||||||
|
// The nil return value indicates the success of the test
|
||||||
|
func TestEndpoint(conn Conn) error {
|
||||||
|
|
||||||
|
// gooidc will try to call the discovery api when creating the provider and that's all we need to check
|
||||||
|
ctx := clientCtx(context.Background(), conn.VerifyCert)
|
||||||
|
_, err := gooidc.NewProvider(ctx, conn.URL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -97,3 +97,16 @@ func TestAuthCodeURL(t *testing.T) {
|
|||||||
assert.Equal(t, "offline", q.Get("access_type"))
|
assert.Equal(t, "offline", q.Get("access_type"))
|
||||||
assert.False(t, strings.Contains(q.Get("scope"), "offline_access"))
|
assert.False(t, strings.Contains(q.Get("scope"), "offline_access"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTestEndpoint(t *testing.T) {
|
||||||
|
c1 := Conn{
|
||||||
|
URL: googleEndpoint,
|
||||||
|
VerifyCert: true,
|
||||||
|
}
|
||||||
|
c2 := Conn{
|
||||||
|
URL: "https://www.baidu.com",
|
||||||
|
VerifyCert: false,
|
||||||
|
}
|
||||||
|
assert.Nil(t, TestEndpoint(c1))
|
||||||
|
assert.NotNil(t, TestEndpoint(c2))
|
||||||
|
}
|
||||||
|
@ -145,6 +145,7 @@ func init() {
|
|||||||
beego.Router("/api/system/gc/schedule", &GCAPI{}, "get:Get;put:Put;post:Post")
|
beego.Router("/api/system/gc/schedule", &GCAPI{}, "get:Get;put:Put;post:Post")
|
||||||
beego.Router("/api/system/scanAll/schedule", &ScanAllAPI{}, "get:Get;put:Put;post:Post")
|
beego.Router("/api/system/scanAll/schedule", &ScanAllAPI{}, "get:Get;put:Put;post:Post")
|
||||||
beego.Router("/api/system/CVEWhitelist", &SysCVEWhitelistAPI{}, "get:Get;put:Put")
|
beego.Router("/api/system/CVEWhitelist", &SysCVEWhitelistAPI{}, "get:Get;put:Put")
|
||||||
|
beego.Router("/api/system/oidc/ping", &OIDCAPI{}, "post:Ping")
|
||||||
|
|
||||||
beego.Router("/api/projects/:pid([0-9]+)/robots/", &RobotAPI{}, "post:Post;get:List")
|
beego.Router("/api/projects/:pid([0-9]+)/robots/", &RobotAPI{}, "post:Post;get:List")
|
||||||
beego.Router("/api/projects/:pid([0-9]+)/robots/:id([0-9]+)", &RobotAPI{}, "get:Get;put:Put;delete:Delete")
|
beego.Router("/api/projects/:pid([0-9]+)/robots/:id([0-9]+)", &RobotAPI{}, "get:Get;put:Put;delete:Delete")
|
||||||
|
56
src/core/api/oidc.go
Normal file
56
src/core/api/oidc.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OIDCAPI handles the requests to /api/system/oidc/xxx
|
||||||
|
type OIDCAPI struct {
|
||||||
|
BaseController
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare validates the request initially
|
||||||
|
func (oa *OIDCAPI) Prepare() {
|
||||||
|
oa.BaseController.Prepare()
|
||||||
|
if !oa.SecurityCtx.IsAuthenticated() {
|
||||||
|
oa.SendUnAuthorizedError(errors.New("unauthorized"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !oa.SecurityCtx.IsSysAdmin() {
|
||||||
|
msg := "only system admin has permission to access this API"
|
||||||
|
log.Errorf(msg)
|
||||||
|
oa.SendForbiddenError(errors.New(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping will handles the request to test connection to OIDC endpoint
|
||||||
|
func (oa *OIDCAPI) Ping() {
|
||||||
|
var c oidc.Conn
|
||||||
|
if err := oa.DecodeJSONReq(&c); err != nil {
|
||||||
|
log.Error("Failed to decode JSON request.")
|
||||||
|
oa.SendBadRequestError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := oidc.TestEndpoint(c); err != nil {
|
||||||
|
log.Errorf("Failed to verify connection: %+v, err: %v", c, err)
|
||||||
|
oa.SendBadRequestError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
69
src/core/api/oidc_test.go
Normal file
69
src/core/api/oidc_test.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright Project Harbor Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/oidc"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOIDCAPI_Ping(t *testing.T) {
|
||||||
|
url := "/api/system/oidc/ping"
|
||||||
|
cases := []*codeCheckingCase{
|
||||||
|
{ // 401
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
bodyJSON: oidc.Conn{},
|
||||||
|
url: url,
|
||||||
|
},
|
||||||
|
code: http.StatusUnauthorized,
|
||||||
|
},
|
||||||
|
{ // 403
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
bodyJSON: oidc.Conn{},
|
||||||
|
url: url,
|
||||||
|
credential: nonSysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{ // 400
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
bodyJSON: oidc.Conn{
|
||||||
|
URL: "https://www.baidu.com",
|
||||||
|
VerifyCert: true,
|
||||||
|
},
|
||||||
|
url: url,
|
||||||
|
credential: sysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{ // 200
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
bodyJSON: oidc.Conn{
|
||||||
|
URL: "https://accounts.google.com",
|
||||||
|
VerifyCert: true,
|
||||||
|
},
|
||||||
|
url: url,
|
||||||
|
credential: sysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusOK,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runCodeCheckingCases(t, cases...)
|
||||||
|
}
|
@ -97,6 +97,7 @@ func initRouters() {
|
|||||||
beego.Router("/api/system/gc/schedule", &api.GCAPI{}, "get:Get;put:Put;post:Post")
|
beego.Router("/api/system/gc/schedule", &api.GCAPI{}, "get:Get;put:Put;post:Post")
|
||||||
beego.Router("/api/system/scanAll/schedule", &api.ScanAllAPI{}, "get:Get;put:Put;post:Post")
|
beego.Router("/api/system/scanAll/schedule", &api.ScanAllAPI{}, "get:Get;put:Put;post:Post")
|
||||||
beego.Router("/api/system/CVEWhitelist", &api.SysCVEWhitelistAPI{}, "get:Get;put:Put")
|
beego.Router("/api/system/CVEWhitelist", &api.SysCVEWhitelistAPI{}, "get:Get;put:Put")
|
||||||
|
beego.Router("/api/system/oidc/ping", &api.OIDCAPI{}, "post:Ping")
|
||||||
|
|
||||||
beego.Router("/api/logs", &api.LogAPI{})
|
beego.Router("/api/logs", &api.LogAPI{})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user