feat: update incoming token to be a JWT token that takes in username

This commit is contained in:
Andrey Melnikov
2020-10-12 16:07:12 -07:00
parent 0e1e48dfc8
commit 7150f24631
10 changed files with 253 additions and 135 deletions

View File

@@ -3,7 +3,7 @@
"info": {
"title": "Onepanel",
"description": "Onepanel API",
"version": "0.13.0",
"version": "v0.14.0",
"contact": {
"name": "Onepanel project",
"url": "https://github.com/onepanelio/core"
@@ -77,7 +77,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/TokenWrapper"
"$ref": "#/definitions/IsValidTokenRequest"
}
}
],
@@ -2967,11 +2967,25 @@
}
}
},
"IsValidTokenRequest": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"token": {
"type": "string"
}
}
},
"IsValidTokenResponse": {
"type": "object",
"properties": {
"domain": {
"type": "string"
},
"jwtToken": {
"type": "string"
}
}
},
@@ -3394,14 +3408,6 @@
}
}
},
"TokenWrapper": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
}
},
"UpdateSecretKeyValueResponse": {
"type": "object",
"properties": {

View File

@@ -204,65 +204,19 @@ func (x *IsAuthorizedResponse) GetAuthorized() bool {
return false
}
type TokenWrapper struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
}
func (x *TokenWrapper) Reset() {
*x = TokenWrapper{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TokenWrapper) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TokenWrapper) ProtoMessage() {}
func (x *TokenWrapper) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TokenWrapper.ProtoReflect.Descriptor instead.
func (*TokenWrapper) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{3}
}
func (x *TokenWrapper) GetToken() string {
if x != nil {
return x.Token
}
return ""
}
type IsValidTokenRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Token *TokenWrapper `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
}
func (x *IsValidTokenRequest) Reset() {
*x = IsValidTokenRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[4]
mi := &file_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -275,7 +229,7 @@ func (x *IsValidTokenRequest) String() string {
func (*IsValidTokenRequest) ProtoMessage() {}
func (x *IsValidTokenRequest) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[4]
mi := &file_auth_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -288,14 +242,21 @@ func (x *IsValidTokenRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use IsValidTokenRequest.ProtoReflect.Descriptor instead.
func (*IsValidTokenRequest) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{4}
return file_auth_proto_rawDescGZIP(), []int{3}
}
func (x *IsValidTokenRequest) GetToken() *TokenWrapper {
func (x *IsValidTokenRequest) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *IsValidTokenRequest) GetToken() string {
if x != nil {
return x.Token
}
return nil
return ""
}
type IsValidTokenResponse struct {
@@ -303,13 +264,14 @@ type IsValidTokenResponse struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
JwtToken string `protobuf:"bytes,2,opt,name=jwtToken,proto3" json:"jwtToken,omitempty"`
}
func (x *IsValidTokenResponse) Reset() {
*x = IsValidTokenResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[5]
mi := &file_auth_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -322,7 +284,7 @@ func (x *IsValidTokenResponse) String() string {
func (*IsValidTokenResponse) ProtoMessage() {}
func (x *IsValidTokenResponse) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[5]
mi := &file_auth_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -335,7 +297,7 @@ func (x *IsValidTokenResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use IsValidTokenResponse.ProtoReflect.Descriptor instead.
func (*IsValidTokenResponse) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{5}
return file_auth_proto_rawDescGZIP(), []int{4}
}
func (x *IsValidTokenResponse) GetDomain() string {
@@ -345,6 +307,13 @@ func (x *IsValidTokenResponse) GetDomain() string {
return ""
}
func (x *IsValidTokenResponse) GetJwtToken() string {
if x != nil {
return x.JwtToken
}
return ""
}
var File_auth_proto protoreflect.FileDescriptor
var file_auth_proto_rawDesc = []byte{
@@ -370,32 +339,32 @@ var file_auth_proto_rawDesc = []byte{
0x7a, 0x65, 0x64, 0x22, 0x36, 0x0a, 0x14, 0x49, 0x73, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
0x7a, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x22, 0x24, 0x0a, 0x0c, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x22, 0x3e, 0x0a, 0x13, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x6f,
0x6b, 0x65, 0x6e, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x22, 0x2e, 0x0a, 0x14, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d,
0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x32, 0xea, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x6c, 0x0a, 0x0c, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65,
0x6e, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x22, 0x18,
0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x75,
0x74, 0x68, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x3a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
0x6d, 0x0a, 0x0c, 0x49, 0x73, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x12,
0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x73, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x49, 0x73, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x12, 0x2f, 0x61,
0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68,
0x3a, 0x0c, 0x69, 0x73, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x22, 0x47, 0x0a, 0x13, 0x49,
0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14,
0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x4a, 0x0a, 0x14, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x32, 0xe6, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x68, 0x0a, 0x0c, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f,
0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x49, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f,
0x61, 0x70, 0x69, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x75, 0x74,
0x68, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x3a, 0x01, 0x2a, 0x12, 0x6d, 0x0a, 0x0c, 0x49, 0x73,
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x49, 0x73, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x73, 0x41, 0x75, 0x74,
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x76,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x3a, 0x0c, 0x69, 0x73, 0x41,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
@@ -410,27 +379,25 @@ func file_auth_proto_rawDescGZIP() []byte {
return file_auth_proto_rawDescData
}
var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_auth_proto_goTypes = []interface{}{
(*IsAuthorized)(nil), // 0: api.IsAuthorized
(*IsAuthorizedRequest)(nil), // 1: api.IsAuthorizedRequest
(*IsAuthorizedResponse)(nil), // 2: api.IsAuthorizedResponse
(*TokenWrapper)(nil), // 3: api.TokenWrapper
(*IsValidTokenRequest)(nil), // 4: api.IsValidTokenRequest
(*IsValidTokenResponse)(nil), // 5: api.IsValidTokenResponse
(*IsValidTokenRequest)(nil), // 3: api.IsValidTokenRequest
(*IsValidTokenResponse)(nil), // 4: api.IsValidTokenResponse
}
var file_auth_proto_depIdxs = []int32{
0, // 0: api.IsAuthorizedRequest.isAuthorized:type_name -> api.IsAuthorized
3, // 1: api.IsValidTokenRequest.token:type_name -> api.TokenWrapper
4, // 2: api.AuthService.IsValidToken:input_type -> api.IsValidTokenRequest
1, // 3: api.AuthService.IsAuthorized:input_type -> api.IsAuthorizedRequest
5, // 4: api.AuthService.IsValidToken:output_type -> api.IsValidTokenResponse
2, // 5: api.AuthService.IsAuthorized:output_type -> api.IsAuthorizedResponse
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
3, // 1: api.AuthService.IsValidToken:input_type -> api.IsValidTokenRequest
1, // 2: api.AuthService.IsAuthorized:input_type -> api.IsAuthorizedRequest
4, // 3: api.AuthService.IsValidToken:output_type -> api.IsValidTokenResponse
2, // 4: api.AuthService.IsAuthorized:output_type -> api.IsAuthorizedResponse
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_auth_proto_init() }
@@ -476,18 +443,6 @@ func file_auth_proto_init() {
}
}
file_auth_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TokenWrapper); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*IsValidTokenRequest); i {
case 0:
return &v.state
@@ -499,7 +454,7 @@ func file_auth_proto_init() {
return nil
}
}
file_auth_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
file_auth_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*IsValidTokenResponse); i {
case 0:
return &v.state
@@ -518,7 +473,7 @@ func file_auth_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_auth_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -39,7 +39,7 @@ func request_AuthService_IsValidToken_0(ctx context.Context, marshaler runtime.M
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Token); err != nil && err != io.EOF {
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@@ -56,7 +56,7 @@ func local_request_AuthService_IsValidToken_0(ctx context.Context, marshaler run
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Token); err != nil && err != io.EOF {
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}

View File

@@ -9,7 +9,7 @@ service AuthService {
rpc IsValidToken(IsValidTokenRequest) returns (IsValidTokenResponse) {
option (google.api.http) = {
post: "/apis/v1beta1/auth/token"
body: "token"
body: "*"
};
}
@@ -37,14 +37,12 @@ message IsAuthorizedResponse {
bool authorized = 1;
}
message TokenWrapper {
string token = 1;
}
message IsValidTokenRequest {
TokenWrapper token = 1;
string username = 1;
string token = 2;
}
message IsValidTokenResponse {
string domain = 1;
string jwtToken = 2;
}

3
go.mod
View File

@@ -43,4 +43,5 @@ require (
k8s.io/apimachinery v0.16.7-beta.0
k8s.io/client-go v0.16.4
sigs.k8s.io/yaml v1.2.0
)
github.com/dgrijalva/jwt-go v3.2.0+incompatible
)

View File

@@ -4,6 +4,7 @@ import (
"fmt"
sq "github.com/Masterminds/squirrel"
argoprojv1alpha1 "github.com/argoproj/argo/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
"github.com/jmoiron/sqlx"
"github.com/onepanelio/core/pkg/util/gcs"
"github.com/onepanelio/core/pkg/util/router"
"github.com/onepanelio/core/pkg/util/s3"
@@ -38,6 +39,24 @@ func NewConfig() (config *Config) {
return
}
// GetDefaultClient loads a default k8s client
func GetDefaultClient() (*Client, error) {
kubeConfig := NewConfig()
client, err := NewClient(kubeConfig, nil, nil)
if err != nil {
return nil, err
}
config, err := client.GetSystemConfig()
if err != nil {
return nil, err
}
dbDriverName, dbDataSourceName := config.DatabaseConnection()
client.DB = NewDB(sqlx.MustConnect(dbDriverName, dbDataSourceName))
return client, nil
}
// NewClient creates a client to interact with the Onepanel system.
// It includes access to the database, kubernetes, argo, and configuration.
func NewClient(config *Config, db *DB, systemConfig SystemConfig) (client *Client, err error) {

View File

@@ -38,6 +38,12 @@ func NewSystemConfig(configMap *ConfigMap, secret *Secret) (config SystemConfig,
}
config["databasePassword"] = string(databasePassword)
hmac, err := base64.StdEncoding.DecodeString(secret.Data["hmac"])
if err != nil {
return
}
config["hmac"] = string(hmac)
return
}
@@ -183,6 +189,16 @@ func (s SystemConfig) UpdateNodePoolOptions(parameters []Parameter) ([]Parameter
return result, nil
}
// HMACKey gets the HMAC value, or nil.
func (s SystemConfig) HMACKey() []byte {
hmac := s.GetValue("hmac")
if hmac == nil {
return []byte{}
}
return []byte(*hmac)
}
// ArtifactRepositoryS3Provider is meant to be used
// by the CLI. CLI will marshal this struct into the correct
// YAML structure for k8s configmap / secret.

48
pkg/util/tokens/jwt.go Normal file
View File

@@ -0,0 +1,48 @@
package tokens
import (
"fmt"
"github.com/dgrijalva/jwt-go"
)
// TokenContent represents the content we store in a JWT token - the username and k8s token
type TokenContent struct {
Username string
Token string
}
// CreateJWTToken creates a jwt token containing a username and another token using the input secret
func CreateJWTToken(username string, token string, secret []byte) (string, error) {
result := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"username": username,
"token": token,
})
// Sign and get the complete encoded token as a string using the secret
return result.SignedString(secret)
}
// ParseJWTToken parses the token string into a TokenContent
func ParseJWTToken(tokenString string, secret []byte) (content *TokenContent, err error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return &TokenContent{
Username: claims["username"].(string),
Token: claims["token"].(string),
}, nil
}
return nil, fmt.Errorf("Unknown error getting token, claim or token is not ok")
}

View File

@@ -2,9 +2,13 @@ package auth
import (
"context"
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"github.com/onepanelio/core/api"
"github.com/onepanelio/core/pkg/util/tokens"
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
"net/http"
"strings"
@@ -64,7 +68,14 @@ func getClient(ctx context.Context, kubeConfig *v1.Config, db *v1.DB, sysConfig
return nil, status.Error(codes.Unauthenticated, `Missing or invalid "authorization" header.`)
}
kubeConfig.BearerToken = *bearerToken
tokenContent, err := tokens.ParseJWTToken(*bearerToken, sysConfig.HMACKey())
if err != nil {
return nil, err
}
sysConfig["jwtToken"] = *bearerToken
kubeConfig.BearerToken = tokenContent.Token
client, err := v1.NewClient(kubeConfig, db, sysConfig)
if err != nil {
return nil, err
@@ -98,6 +109,43 @@ func IsAuthorized(c *v1.Client, namespace, verb, group, resource, name string) (
return
}
func verifyLogin(client *v1.Client, tokenRequest *api.IsValidTokenRequest) (rawToken string, err error) {
accountsList, err := client.CoreV1().ServiceAccounts("onepanel").List(v1.ListOptions{})
if err != nil {
return "", err
}
authTokenSecretName := ""
for _, serviceAccount := range accountsList.Items {
if serviceAccount.Name != tokenRequest.Username {
continue
}
for _, secret := range serviceAccount.Secrets {
if strings.Contains(secret.Name, "-token-") {
authTokenSecretName = secret.Name
break
}
}
}
if authTokenSecretName == "" {
return "", fmt.Errorf("Unknown service account '%v'", tokenRequest.Username)
}
secret, err := client.CoreV1().Secrets("onepanel").Get(authTokenSecretName, v12.GetOptions{})
if err != nil {
return "", err
}
currentTokenBytes := md5.Sum(secret.Data["token"])
currentTokenString := hex.EncodeToString(currentTokenBytes[:])
if tokenRequest.Token != fmt.Sprintf("%s", currentTokenString) {
return "", fmt.Errorf("tokens doesn't match what's on record.")
}
return string(secret.Data["token"]), nil
}
// UnaryInterceptor performs authentication checks.
// The two main cases are:
// 1. Is the token valid? This is used for logging in.
@@ -113,10 +161,35 @@ func UnaryInterceptor(kubeConfig *v1.Config, db *v1.DB, sysConfig v1.SystemConfi
tokenRequest, ok := req.(*api.IsValidTokenRequest)
if !ok {
return resp, errors.New("IsValidToken does not have correct request type")
return resp, errors.New("LogInRequest does not have correct request type")
}
md.Set("authorization", tokenRequest.Token.Token)
defaultClient, err := v1.GetDefaultClient()
if err != nil {
return nil, err
}
rawToken, err := verifyLogin(defaultClient, tokenRequest)
if err != nil {
return nil, err
}
sysConfig, err := defaultClient.GetSystemConfig()
if err != nil {
return nil, err
}
hmac := sysConfig.HMACKey()
if len(hmac) == 0 {
return nil, errors.New("HMAC key not found in secrets. This value is required.")
}
jwtToken, err := tokens.CreateJWTToken(tokenRequest.Username, rawToken, hmac)
if err != nil {
return nil, err
}
md.Set("onepanel-auth-token", jwtToken)
ctx, err = getClient(ctx, kubeConfig, db, sysConfig)
if err != nil {

View File

@@ -66,8 +66,10 @@ func (a *AuthServer) IsValidToken(ctx context.Context, req *api.IsValidTokenRequ
if err != nil {
return
}
res = &api.IsValidTokenResponse{}
res.Domain = config["ONEPANEL_DOMAIN"]
res = &api.IsValidTokenResponse{
Domain: config["ONEPANEL_DOMAIN"],
JwtToken: config["jwtToken"],
}
return res, nil
}