Merge pull request #2354 from influxdata/multitenancy_whitelist_only

Add Public toggle to deny authz to users not explicitly added to an Organization
pull/2330/merge
Jared Scheib 2017-11-13 20:25:23 -08:00 committed by GitHub
commit 96a362cb06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 578 additions and 200 deletions

View File

@ -514,6 +514,7 @@ func MarshalOrganization(o *chronograf.Organization) ([]byte, error) {
ID: o.ID,
Name: o.Name,
DefaultRole: o.DefaultRole,
Public: o.Public,
})
}
@ -531,6 +532,7 @@ func UnmarshalOrganization(data []byte, o *chronograf.Organization) error {
o.ID = pb.ID
o.Name = pb.Name
o.DefaultRole = pb.DefaultRole
o.Public = pb.Public
return nil
}

View File

@ -935,6 +935,7 @@ type Organization struct {
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
DefaultRole string `protobuf:"bytes,3,opt,name=DefaultRole,proto3" json:"DefaultRole,omitempty"`
Public bool `protobuf:"varint,4,opt,name=Public,proto3" json:"Public,omitempty"`
}
func (m *Organization) Reset() { *m = Organization{} }
@ -963,6 +964,13 @@ func (m *Organization) GetDefaultRole() string {
return ""
}
func (m *Organization) GetPublic() bool {
if m != nil {
return m.Public
}
return true
}
func init() {
proto.RegisterType((*Source)(nil), "internal.Source")
proto.RegisterType((*Dashboard)(nil), "internal.Dashboard")
@ -985,77 +993,78 @@ func init() {
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
var fileDescriptorInternal = []byte{
// 1148 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x8e, 0xe3, 0xc4,
0x13, 0x56, 0xc7, 0x71, 0x12, 0x57, 0x66, 0xf7, 0xf7, 0x53, 0x6b, 0xc5, 0x9a, 0x45, 0x42, 0xc1,
0x02, 0x29, 0x48, 0xec, 0x80, 0x76, 0x85, 0x84, 0x38, 0x20, 0x65, 0x26, 0x68, 0x35, 0xec, 0xbf,
0xa1, 0x33, 0xb3, 0x9c, 0xd0, 0xaa, 0xe3, 0x54, 0x12, 0x6b, 0x9d, 0xd8, 0xb4, 0xed, 0x99, 0x98,
0xb7, 0x41, 0xe2, 0xc4, 0x11, 0x71, 0x47, 0xe2, 0x84, 0xf6, 0x41, 0x78, 0x0e, 0x54, 0xdd, 0x6d,
0xc7, 0xd9, 0x84, 0xd5, 0x5c, 0xe0, 0xd6, 0x5f, 0x55, 0x77, 0x75, 0x57, 0xd5, 0x57, 0x9f, 0x1a,
0x6e, 0x47, 0xeb, 0x1c, 0xd5, 0x5a, 0xc6, 0xc7, 0xa9, 0x4a, 0xf2, 0x84, 0xf7, 0x2a, 0x1c, 0xfc,
0xd5, 0x82, 0xce, 0x24, 0x29, 0x54, 0x88, 0xfc, 0x36, 0xb4, 0xce, 0xc6, 0x3e, 0x1b, 0xb0, 0xa1,
0x23, 0x5a, 0x67, 0x63, 0xce, 0xa1, 0xfd, 0x4c, 0xae, 0xd0, 0x6f, 0x0d, 0xd8, 0xd0, 0x13, 0x7a,
0x4d, 0xb6, 0x8b, 0x32, 0x45, 0xdf, 0x31, 0x36, 0x5a, 0xf3, 0x7b, 0xd0, 0xbb, 0xcc, 0x28, 0xda,
0x0a, 0xfd, 0xb6, 0xb6, 0xd7, 0x98, 0x7c, 0xe7, 0x32, 0xcb, 0xae, 0x13, 0x35, 0xf3, 0x5d, 0xe3,
0xab, 0x30, 0xff, 0x3f, 0x38, 0x97, 0xe2, 0x89, 0xdf, 0xd1, 0x66, 0x5a, 0x72, 0x1f, 0xba, 0x63,
0x9c, 0xcb, 0x22, 0xce, 0xfd, 0xee, 0x80, 0x0d, 0x7b, 0xa2, 0x82, 0x14, 0xe7, 0x02, 0x63, 0x5c,
0x28, 0x39, 0xf7, 0x7b, 0x26, 0x4e, 0x85, 0xf9, 0x31, 0xf0, 0xb3, 0x75, 0x86, 0x61, 0xa1, 0x70,
0xf2, 0x2a, 0x4a, 0x5f, 0xa0, 0x8a, 0xe6, 0xa5, 0xef, 0xe9, 0x00, 0x07, 0x3c, 0x74, 0xcb, 0x53,
0xcc, 0x25, 0xdd, 0x0d, 0x3a, 0x54, 0x05, 0x79, 0x00, 0x47, 0x93, 0xa5, 0x54, 0x38, 0x9b, 0x60,
0xa8, 0x30, 0xf7, 0xfb, 0xda, 0xbd, 0x63, 0xa3, 0x3d, 0xcf, 0xd5, 0x42, 0xae, 0xa3, 0x1f, 0x65,
0x1e, 0x25, 0x6b, 0xff, 0xc8, 0xec, 0x69, 0xda, 0xa8, 0x4a, 0x22, 0x89, 0xd1, 0xbf, 0x65, 0xaa,
0x44, 0xeb, 0xe0, 0x37, 0x06, 0xde, 0x58, 0x66, 0xcb, 0x69, 0x22, 0xd5, 0xec, 0x46, 0xb5, 0xbe,
0x0f, 0x6e, 0x88, 0x71, 0x9c, 0xf9, 0xce, 0xc0, 0x19, 0xf6, 0x1f, 0xdc, 0x3d, 0xae, 0x9b, 0x58,
0xc7, 0x39, 0xc5, 0x38, 0x16, 0x66, 0x17, 0xff, 0x0c, 0xbc, 0x1c, 0x57, 0x69, 0x2c, 0x73, 0xcc,
0xfc, 0xb6, 0x3e, 0xc2, 0xb7, 0x47, 0x2e, 0xac, 0x4b, 0x6c, 0x37, 0xed, 0xa5, 0xe2, 0xee, 0xa7,
0x12, 0xfc, 0xd2, 0x82, 0x5b, 0x3b, 0xd7, 0xf1, 0x23, 0x60, 0x1b, 0xfd, 0x72, 0x57, 0xb0, 0x0d,
0xa1, 0x52, 0xbf, 0xda, 0x15, 0xac, 0x24, 0x74, 0xad, 0xb9, 0xe1, 0x0a, 0x76, 0x4d, 0x68, 0xa9,
0x19, 0xe1, 0x0a, 0xb6, 0xe4, 0x1f, 0x43, 0xf7, 0x87, 0x02, 0x55, 0x84, 0x99, 0xef, 0xea, 0xd7,
0xfd, 0x6f, 0xfb, 0xba, 0x6f, 0x0b, 0x54, 0xa5, 0xa8, 0xfc, 0x54, 0x0d, 0xcd, 0x26, 0x43, 0x0d,
0xbd, 0x26, 0x5b, 0x4e, 0xcc, 0xeb, 0x1a, 0x1b, 0xad, 0x6d, 0x15, 0x0d, 0x1f, 0xa8, 0x8a, 0x9f,
0x43, 0x5b, 0x6e, 0x30, 0xf3, 0x3d, 0x1d, 0xff, 0x83, 0x7f, 0x28, 0xd8, 0xf1, 0x68, 0x83, 0xd9,
0xd7, 0xeb, 0x5c, 0x95, 0x42, 0x6f, 0xbf, 0xf7, 0x08, 0xbc, 0xda, 0x44, 0xac, 0x7c, 0x85, 0xa5,
0x4e, 0xd0, 0x13, 0xb4, 0xe4, 0x1f, 0x82, 0x7b, 0x25, 0xe3, 0xc2, 0x34, 0xa7, 0xff, 0xe0, 0xf6,
0x36, 0xec, 0x68, 0x13, 0x65, 0xc2, 0x38, 0xbf, 0x6c, 0x7d, 0xc1, 0x82, 0x5f, 0x19, 0xb4, 0xc9,
0x46, 0x95, 0x8d, 0x71, 0x21, 0xc3, 0xf2, 0x24, 0x29, 0xd6, 0xb3, 0xcc, 0x67, 0x03, 0x67, 0xe8,
0x88, 0x1d, 0x1b, 0x7f, 0x07, 0x3a, 0x53, 0xe3, 0x6d, 0x0d, 0x9c, 0xa1, 0x27, 0x2c, 0xe2, 0x77,
0xc0, 0x8d, 0xe5, 0x14, 0x63, 0x3b, 0x63, 0x06, 0xd0, 0xee, 0x54, 0xe1, 0x3c, 0xda, 0xd8, 0x11,
0xb3, 0x88, 0xec, 0x59, 0x31, 0x27, 0xbb, 0xe9, 0x9e, 0x45, 0x54, 0xae, 0xa9, 0xcc, 0xea, 0x12,
0xd2, 0x9a, 0x22, 0x67, 0xa1, 0x8c, 0xab, 0x1a, 0x1a, 0x10, 0xfc, 0xce, 0x68, 0xb6, 0x0c, 0x27,
0x1a, 0xbc, 0x34, 0x15, 0x7d, 0x17, 0x7a, 0xc4, 0x97, 0x97, 0x57, 0x52, 0x59, 0x6e, 0x76, 0x09,
0xbf, 0x90, 0x8a, 0x7f, 0x0a, 0x1d, 0x9d, 0xf9, 0x01, 0x7e, 0x56, 0xe1, 0x5e, 0x90, 0x5f, 0xd8,
0x6d, 0x75, 0x07, 0xdb, 0x8d, 0x0e, 0xd6, 0xc9, 0xba, 0xcd, 0x64, 0xef, 0x83, 0x4b, 0x54, 0x28,
0xf5, 0xeb, 0x0f, 0x46, 0x36, 0x84, 0x31, 0xbb, 0x82, 0x4b, 0xb8, 0xb5, 0x73, 0x63, 0x7d, 0x13,
0xdb, 0xbd, 0x69, 0xdb, 0x45, 0xcf, 0x76, 0x8d, 0x74, 0x25, 0xc3, 0x18, 0xc3, 0x1c, 0x67, 0xba,
0xde, 0x3d, 0x51, 0xe3, 0xe0, 0x27, 0xb6, 0x8d, 0xab, 0xef, 0x23, 0xe5, 0x08, 0x93, 0xd5, 0x4a,
0xae, 0x67, 0x36, 0x74, 0x05, 0xa9, 0x6e, 0xb3, 0xa9, 0x0d, 0xdd, 0x9a, 0x4d, 0x09, 0xab, 0xd4,
0x76, 0xb0, 0xa5, 0x52, 0x3e, 0x80, 0xfe, 0x0a, 0x65, 0x56, 0x28, 0x5c, 0xe1, 0x3a, 0xb7, 0x25,
0x68, 0x9a, 0xf8, 0x5d, 0xe8, 0xe6, 0x72, 0xf1, 0x92, 0xb8, 0x67, 0x3b, 0x99, 0xcb, 0xc5, 0x63,
0x2c, 0xf9, 0x7b, 0xe0, 0xcd, 0x23, 0x8c, 0x67, 0xda, 0x65, 0xda, 0xd9, 0xd3, 0x86, 0xc7, 0x58,
0x06, 0x7f, 0x32, 0xe8, 0x4c, 0x50, 0x5d, 0xa1, 0xba, 0x91, 0xa4, 0x34, 0xa5, 0xda, 0x79, 0x8b,
0x54, 0xb7, 0x0f, 0x4b, 0xb5, 0xbb, 0x95, 0xea, 0x3b, 0xe0, 0x4e, 0x54, 0x78, 0x36, 0xd6, 0x2f,
0x72, 0x84, 0x01, 0xc4, 0xc6, 0x51, 0x98, 0x47, 0x57, 0x68, 0xf5, 0xdb, 0xa2, 0x3d, 0xa5, 0xe9,
0x1d, 0x50, 0x9a, 0x3f, 0x18, 0x74, 0x9e, 0xc8, 0x32, 0x29, 0xf2, 0x3d, 0x16, 0x0e, 0xa0, 0x3f,
0x4a, 0xd3, 0x38, 0x0a, 0xcd, 0x69, 0x93, 0x51, 0xd3, 0x44, 0x3b, 0x9e, 0x36, 0xea, 0x6b, 0x72,
0x6b, 0x9a, 0x68, 0x8a, 0x4f, 0xb5, 0x9a, 0x1a, 0x69, 0x6c, 0x4c, 0xb1, 0x11, 0x51, 0xed, 0xa4,
0x22, 0x8c, 0x8a, 0x3c, 0x99, 0xc7, 0xc9, 0xb5, 0xce, 0xb6, 0x27, 0x6a, 0xbc, 0x97, 0x44, 0xe7,
0x40, 0x12, 0xaf, 0x5b, 0xd0, 0xfe, 0xaf, 0x54, 0xf2, 0x08, 0x58, 0x64, 0x1f, 0xc1, 0xa2, 0x5a,
0x33, 0xbb, 0x0d, 0xcd, 0xf4, 0xa1, 0x5b, 0x2a, 0xb9, 0x5e, 0x60, 0xe6, 0xf7, 0xb4, 0x02, 0x55,
0x50, 0x7b, 0xf4, 0xac, 0x19, 0xb1, 0xf4, 0x44, 0x05, 0xeb, 0xd9, 0x81, 0xc6, 0xec, 0x7c, 0x62,
0x75, 0xb5, 0xaf, 0x5f, 0xe4, 0xef, 0x96, 0xee, 0xdf, 0x93, 0xd3, 0xd7, 0x0c, 0xdc, 0x7a, 0xf0,
0x4e, 0x77, 0x07, 0xef, 0x74, 0x3b, 0x78, 0xe3, 0x93, 0x6a, 0xf0, 0xc6, 0x27, 0x84, 0xc5, 0x79,
0x35, 0x78, 0xe2, 0x9c, 0x1a, 0xfa, 0x48, 0x25, 0x45, 0x7a, 0x52, 0x9a, 0xce, 0x7b, 0xa2, 0xc6,
0xc4, 0xd6, 0xef, 0x96, 0xa8, 0x6c, 0xa9, 0x3d, 0x61, 0x11, 0x71, 0xfb, 0x89, 0x16, 0x25, 0x53,
0x5c, 0x03, 0xf8, 0x47, 0xe0, 0x0a, 0x2a, 0x9e, 0xae, 0xf0, 0x4e, 0x5f, 0xb4, 0x59, 0x18, 0x2f,
0x05, 0x35, 0xff, 0x29, 0x4b, 0x72, 0x8b, 0x82, 0x87, 0xf6, 0x38, 0x45, 0xbf, 0x4c, 0x53, 0x54,
0x76, 0x54, 0x0d, 0xd0, 0x77, 0x26, 0xd7, 0x68, 0x54, 0xd6, 0x11, 0x06, 0x04, 0xdf, 0x83, 0x37,
0x8a, 0x51, 0xe5, 0xa2, 0x88, 0xf7, 0xb5, 0x99, 0x43, 0xfb, 0x9b, 0xc9, 0xf3, 0x67, 0xd5, 0x80,
0xd3, 0x7a, 0x3b, 0x96, 0xce, 0x1b, 0x63, 0xf9, 0x58, 0xa6, 0xf2, 0x6c, 0xac, 0x79, 0xe6, 0x08,
0x8b, 0x82, 0x9f, 0x19, 0xb4, 0x69, 0xfe, 0x1b, 0xa1, 0xdb, 0x6f, 0xd3, 0x8e, 0x73, 0x95, 0x5c,
0x45, 0x33, 0x54, 0x95, 0x76, 0x54, 0x58, 0x27, 0x1d, 0x2e, 0xb1, 0xfe, 0x00, 0x5a, 0x44, 0xbd,
0xa6, 0xcf, 0x4f, 0xc5, 0xe5, 0x46, 0xaf, 0xc9, 0x2c, 0x8c, 0x93, 0xbf, 0x0f, 0x30, 0x29, 0x52,
0x54, 0xa3, 0xd9, 0x2a, 0x32, 0x63, 0xd5, 0x13, 0x0d, 0x4b, 0xf0, 0x95, 0xf9, 0x4e, 0xed, 0x0d,
0x20, 0x3b, 0xfc, 0xf5, 0x7a, 0xf3, 0xe5, 0xc1, 0xc5, 0xee, 0xb9, 0x1b, 0x65, 0x3b, 0x80, 0xbe,
0xfd, 0x7b, 0xea, 0x9f, 0x9c, 0x15, 0x94, 0x86, 0x69, 0xda, 0xd1, 0x5f, 0xe9, 0x87, 0x7f, 0x07,
0x00, 0x00, 0xff, 0xff, 0x90, 0x83, 0x07, 0x1e, 0x5c, 0x0b, 0x00, 0x00,
// 1166 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x5f, 0x8f, 0xdb, 0x44,
0x10, 0x97, 0xe3, 0x38, 0xb1, 0x27, 0x77, 0x05, 0xad, 0x2a, 0x6a, 0x8a, 0x84, 0x82, 0x55, 0xa4,
0x20, 0xd1, 0x03, 0xb5, 0x42, 0x42, 0x3c, 0x20, 0xe5, 0x2e, 0xa8, 0x3a, 0xfa, 0xef, 0xd8, 0xdc,
0x5d, 0x9f, 0x50, 0xb5, 0x71, 0x26, 0x89, 0x55, 0x27, 0x36, 0x6b, 0xfb, 0x2e, 0xee, 0xb7, 0x41,
0xe2, 0x89, 0x47, 0xc4, 0x3b, 0x12, 0x4f, 0xa8, 0x1f, 0x84, 0xcf, 0x81, 0x66, 0x77, 0xed, 0x38,
0x4d, 0xa8, 0xee, 0x05, 0xde, 0xf6, 0x37, 0xb3, 0x3b, 0xbb, 0x33, 0xf3, 0x9b, 0x9f, 0x16, 0x6e,
0x45, 0xab, 0x1c, 0xe5, 0x4a, 0xc4, 0x47, 0xa9, 0x4c, 0xf2, 0x84, 0xb9, 0x15, 0x0e, 0xfe, 0x6e,
0x41, 0x67, 0x9c, 0x14, 0x32, 0x44, 0x76, 0x0b, 0x5a, 0xa7, 0x23, 0xdf, 0xea, 0x5b, 0x03, 0x9b,
0xb7, 0x4e, 0x47, 0x8c, 0x41, 0xfb, 0x99, 0x58, 0xa2, 0xdf, 0xea, 0x5b, 0x03, 0x8f, 0xab, 0x35,
0xd9, 0xce, 0xcb, 0x14, 0x7d, 0x5b, 0xdb, 0x68, 0xcd, 0xee, 0x82, 0x7b, 0x91, 0x51, 0xb4, 0x25,
0xfa, 0x6d, 0x65, 0xaf, 0x31, 0xf9, 0xce, 0x44, 0x96, 0x5d, 0x27, 0x72, 0xea, 0x3b, 0xda, 0x57,
0x61, 0xf6, 0x3e, 0xd8, 0x17, 0xfc, 0x89, 0xdf, 0x51, 0x66, 0x5a, 0x32, 0x1f, 0xba, 0x23, 0x9c,
0x89, 0x22, 0xce, 0xfd, 0x6e, 0xdf, 0x1a, 0xb8, 0xbc, 0x82, 0x14, 0xe7, 0x1c, 0x63, 0x9c, 0x4b,
0x31, 0xf3, 0x5d, 0x1d, 0xa7, 0xc2, 0xec, 0x08, 0xd8, 0xe9, 0x2a, 0xc3, 0xb0, 0x90, 0x38, 0x7e,
0x15, 0xa5, 0x97, 0x28, 0xa3, 0x59, 0xe9, 0x7b, 0x2a, 0xc0, 0x1e, 0x0f, 0xdd, 0xf2, 0x14, 0x73,
0x41, 0x77, 0x83, 0x0a, 0x55, 0x41, 0x16, 0xc0, 0xc1, 0x78, 0x21, 0x24, 0x4e, 0xc7, 0x18, 0x4a,
0xcc, 0xfd, 0x9e, 0x72, 0x6f, 0xd9, 0x68, 0xcf, 0x73, 0x39, 0x17, 0xab, 0xe8, 0xb5, 0xc8, 0xa3,
0x64, 0xe5, 0x1f, 0xe8, 0x3d, 0x4d, 0x1b, 0x55, 0x89, 0x27, 0x31, 0xfa, 0x87, 0xba, 0x4a, 0xb4,
0x0e, 0x7e, 0xb7, 0xc0, 0x1b, 0x89, 0x6c, 0x31, 0x49, 0x84, 0x9c, 0xde, 0xa8, 0xd6, 0xf7, 0xc1,
0x09, 0x31, 0x8e, 0x33, 0xdf, 0xee, 0xdb, 0x83, 0xde, 0x83, 0x3b, 0x47, 0x75, 0x13, 0xeb, 0x38,
0x27, 0x18, 0xc7, 0x5c, 0xef, 0x62, 0x5f, 0x82, 0x97, 0xe3, 0x32, 0x8d, 0x45, 0x8e, 0x99, 0xdf,
0x56, 0x47, 0xd8, 0xe6, 0xc8, 0xb9, 0x71, 0xf1, 0xcd, 0xa6, 0x9d, 0x54, 0x9c, 0xdd, 0x54, 0x82,
0x5f, 0x5b, 0x70, 0xb8, 0x75, 0x1d, 0x3b, 0x00, 0x6b, 0xad, 0x5e, 0xee, 0x70, 0x6b, 0x4d, 0xa8,
0x54, 0xaf, 0x76, 0xb8, 0x55, 0x12, 0xba, 0x56, 0xdc, 0x70, 0xb8, 0x75, 0x4d, 0x68, 0xa1, 0x18,
0xe1, 0x70, 0x6b, 0xc1, 0x3e, 0x83, 0xee, 0x4f, 0x05, 0xca, 0x08, 0x33, 0xdf, 0x51, 0xaf, 0x7b,
0x6f, 0xf3, 0xba, 0x1f, 0x0a, 0x94, 0x25, 0xaf, 0xfc, 0x54, 0x0d, 0xc5, 0x26, 0x4d, 0x0d, 0xb5,
0x26, 0x5b, 0x4e, 0xcc, 0xeb, 0x6a, 0x1b, 0xad, 0x4d, 0x15, 0x35, 0x1f, 0xa8, 0x8a, 0x5f, 0x41,
0x5b, 0xac, 0x31, 0xf3, 0x3d, 0x15, 0xff, 0x93, 0x7f, 0x29, 0xd8, 0xd1, 0x70, 0x8d, 0xd9, 0x77,
0xab, 0x5c, 0x96, 0x5c, 0x6d, 0xbf, 0xfb, 0x08, 0xbc, 0xda, 0x44, 0xac, 0x7c, 0x85, 0xa5, 0x4a,
0xd0, 0xe3, 0xb4, 0x64, 0xf7, 0xc0, 0xb9, 0x12, 0x71, 0xa1, 0x9b, 0xd3, 0x7b, 0x70, 0x6b, 0x13,
0x76, 0xb8, 0x8e, 0x32, 0xae, 0x9d, 0xdf, 0xb4, 0xbe, 0xb6, 0x82, 0xdf, 0x2c, 0x68, 0x93, 0x8d,
0x2a, 0x1b, 0xe3, 0x5c, 0x84, 0xe5, 0x71, 0x52, 0xac, 0xa6, 0x99, 0x6f, 0xf5, 0xed, 0x81, 0xcd,
0xb7, 0x6c, 0xec, 0x03, 0xe8, 0x4c, 0xb4, 0xb7, 0xd5, 0xb7, 0x07, 0x1e, 0x37, 0x88, 0xdd, 0x06,
0x27, 0x16, 0x13, 0x8c, 0xcd, 0x8c, 0x69, 0x40, 0xbb, 0x53, 0x89, 0xb3, 0x68, 0x6d, 0x46, 0xcc,
0x20, 0xb2, 0x67, 0xc5, 0x8c, 0xec, 0xba, 0x7b, 0x06, 0x51, 0xb9, 0x26, 0x22, 0xab, 0x4b, 0x48,
0x6b, 0x8a, 0x9c, 0x85, 0x22, 0xae, 0x6a, 0xa8, 0x41, 0xf0, 0x87, 0x45, 0xb3, 0xa5, 0x39, 0xd1,
0xe0, 0xa5, 0xae, 0xe8, 0x87, 0xe0, 0x12, 0x5f, 0x5e, 0x5e, 0x09, 0x69, 0xb8, 0xd9, 0x25, 0x7c,
0x29, 0x24, 0xfb, 0x02, 0x3a, 0x2a, 0xf3, 0x3d, 0xfc, 0xac, 0xc2, 0x5d, 0x92, 0x9f, 0x9b, 0x6d,
0x75, 0x07, 0xdb, 0x8d, 0x0e, 0xd6, 0xc9, 0x3a, 0xcd, 0x64, 0xef, 0x83, 0x43, 0x54, 0x28, 0xd5,
0xeb, 0xf7, 0x46, 0xd6, 0x84, 0xd1, 0xbb, 0x82, 0x0b, 0x38, 0xdc, 0xba, 0xb1, 0xbe, 0xc9, 0xda,
0xbe, 0x69, 0xd3, 0x45, 0xcf, 0x74, 0x8d, 0x74, 0x25, 0xc3, 0x18, 0xc3, 0x1c, 0xa7, 0xaa, 0xde,
0x2e, 0xaf, 0x71, 0xf0, 0xb3, 0xb5, 0x89, 0xab, 0xee, 0x23, 0xe5, 0x08, 0x93, 0xe5, 0x52, 0xac,
0xa6, 0x26, 0x74, 0x05, 0xa9, 0x6e, 0xd3, 0x89, 0x09, 0xdd, 0x9a, 0x4e, 0x08, 0xcb, 0xd4, 0x74,
0xb0, 0x25, 0x53, 0xd6, 0x87, 0xde, 0x12, 0x45, 0x56, 0x48, 0x5c, 0xe2, 0x2a, 0x37, 0x25, 0x68,
0x9a, 0xd8, 0x1d, 0xe8, 0xe6, 0x62, 0xfe, 0x92, 0xb8, 0x67, 0x3a, 0x99, 0x8b, 0xf9, 0x63, 0x2c,
0xd9, 0x47, 0xe0, 0xcd, 0x22, 0x8c, 0xa7, 0xca, 0xa5, 0xdb, 0xe9, 0x2a, 0xc3, 0x63, 0x2c, 0x83,
0xbf, 0x2c, 0xe8, 0x8c, 0x51, 0x5e, 0xa1, 0xbc, 0x91, 0xa4, 0x34, 0xa5, 0xda, 0x7e, 0x87, 0x54,
0xb7, 0xf7, 0x4b, 0xb5, 0xb3, 0x91, 0xea, 0xdb, 0xe0, 0x8c, 0x65, 0x78, 0x3a, 0x52, 0x2f, 0xb2,
0xb9, 0x06, 0xc4, 0xc6, 0x61, 0x98, 0x47, 0x57, 0x68, 0xf4, 0xdb, 0xa0, 0x1d, 0xa5, 0x71, 0xf7,
0x28, 0xcd, 0x9f, 0x16, 0x74, 0x9e, 0x88, 0x32, 0x29, 0xf2, 0x1d, 0x16, 0xf6, 0xa1, 0x37, 0x4c,
0xd3, 0x38, 0x0a, 0xf5, 0x69, 0x9d, 0x51, 0xd3, 0x44, 0x3b, 0x9e, 0x36, 0xea, 0xab, 0x73, 0x6b,
0x9a, 0x68, 0x8a, 0x4f, 0x94, 0x9a, 0x6a, 0x69, 0x6c, 0x4c, 0xb1, 0x16, 0x51, 0xe5, 0xa4, 0x22,
0x0c, 0x8b, 0x3c, 0x99, 0xc5, 0xc9, 0xb5, 0xca, 0xd6, 0xe5, 0x35, 0xde, 0x49, 0xa2, 0xb3, 0x27,
0x89, 0x37, 0x2d, 0x68, 0xff, 0x5f, 0x2a, 0x79, 0x00, 0x56, 0x64, 0x1e, 0x61, 0x45, 0xb5, 0x66,
0x76, 0x1b, 0x9a, 0xe9, 0x43, 0xb7, 0x94, 0x62, 0x35, 0xc7, 0xcc, 0x77, 0x95, 0x02, 0x55, 0x50,
0x79, 0xd4, 0xac, 0x69, 0xb1, 0xf4, 0x78, 0x05, 0xeb, 0xd9, 0x81, 0xc6, 0xec, 0x7c, 0x6e, 0x74,
0xb5, 0xa7, 0x5e, 0xe4, 0x6f, 0x97, 0xee, 0xbf, 0x93, 0xd3, 0x37, 0x16, 0x38, 0xf5, 0xe0, 0x9d,
0x6c, 0x0f, 0xde, 0xc9, 0x66, 0xf0, 0x46, 0xc7, 0xd5, 0xe0, 0x8d, 0x8e, 0x09, 0xf3, 0xb3, 0x6a,
0xf0, 0xf8, 0x19, 0x35, 0xf4, 0x91, 0x4c, 0x8a, 0xf4, 0xb8, 0xd4, 0x9d, 0xf7, 0x78, 0x8d, 0x89,
0xad, 0x2f, 0x16, 0x28, 0x4d, 0xa9, 0x3d, 0x6e, 0x10, 0x71, 0xfb, 0x89, 0x12, 0x25, 0x5d, 0x5c,
0x0d, 0xd8, 0xa7, 0xe0, 0x70, 0x2a, 0x9e, 0xaa, 0xf0, 0x56, 0x5f, 0x94, 0x99, 0x6b, 0x2f, 0x05,
0xd5, 0xff, 0x29, 0x43, 0x72, 0x83, 0x82, 0x87, 0xe6, 0x38, 0x45, 0xbf, 0x48, 0x53, 0x94, 0x66,
0x54, 0x35, 0x50, 0x77, 0x26, 0xd7, 0xa8, 0x55, 0xd6, 0xe6, 0x1a, 0x04, 0x3f, 0x82, 0x37, 0x8c,
0x51, 0xe6, 0xbc, 0x88, 0x77, 0xb5, 0x99, 0x41, 0xfb, 0xfb, 0xf1, 0xf3, 0x67, 0xd5, 0x80, 0xd3,
0x7a, 0x33, 0x96, 0xf6, 0x5b, 0x63, 0xf9, 0x58, 0xa4, 0xe2, 0x74, 0xa4, 0x78, 0x66, 0x73, 0x83,
0x82, 0x5f, 0x2c, 0x68, 0xd3, 0xfc, 0x37, 0x42, 0xb7, 0xdf, 0xa5, 0x1d, 0x67, 0x32, 0xb9, 0x8a,
0xa6, 0x28, 0x2b, 0xed, 0xa8, 0xb0, 0x4a, 0x3a, 0x5c, 0x60, 0xfd, 0x01, 0x34, 0x88, 0x7a, 0x4d,
0x9f, 0x9f, 0x8a, 0xcb, 0x8d, 0x5e, 0x93, 0x99, 0x6b, 0x27, 0xfb, 0x18, 0x60, 0x5c, 0xa4, 0x28,
0x87, 0xd3, 0x65, 0xa4, 0xc7, 0xca, 0xe5, 0x0d, 0x4b, 0xf0, 0xad, 0xfe, 0x4e, 0xed, 0x0c, 0xa0,
0xb5, 0xff, 0xeb, 0xf5, 0xf6, 0xcb, 0x83, 0xd7, 0xdb, 0xe7, 0x6e, 0x94, 0x6d, 0x1f, 0x7a, 0xe6,
0xef, 0xa9, 0x7e, 0x72, 0x46, 0x50, 0x1a, 0x26, 0x76, 0x0f, 0x0e, 0x5f, 0x2c, 0xa2, 0x1c, 0xe3,
0x28, 0xcb, 0x9f, 0xaf, 0xe2, 0x52, 0xa5, 0xee, 0xf2, 0x6d, 0xe3, 0xa4, 0xa3, 0x3e, 0xdc, 0x0f,
0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x52, 0x79, 0x4d, 0x4a, 0x82, 0x0b, 0x00, 0x00,
}

View File

@ -146,6 +146,7 @@ message Organization {
uint64 ID = 1; // ID is the unique ID of the organization
string Name = 2; // Name is the organization's name
string DefaultRole = 3; // DefaultRole is the name of the role that is the default for any users added to the organization
bool Public = 4; // Public specifies that users must be explicitly added to the organization
}
// The following is a vim modeline, it autoconfigures vim to have the

View File

@ -23,6 +23,8 @@ const (
DefaultOrganizationName string = "Default"
// DefaultOrganizationRole is the DefaultRole for the Default organization
DefaultOrganizationRole string = "member"
// DefaultOrganizationPublic is the Public setting for the Default organization.
DefaultOrganizationPublic bool = true
)
// OrganizationsStore uses bolt to store and retrieve Organizations
@ -41,6 +43,7 @@ func (s *OrganizationsStore) CreateDefault(ctx context.Context) error {
ID: DefaultOrganizationID,
Name: DefaultOrganizationName,
DefaultRole: DefaultOrganizationRole,
Public: DefaultOrganizationPublic,
}
return s.client.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(OrganizationsBucket)

View File

@ -171,15 +171,19 @@ func TestOrganizationsStore_All(t *testing.T) {
addFirst bool
}{
{
name: "Get Organization",
name: "Get Organizations",
args: args{
ctx: context.Background(),
orgs: []chronograf.Organization{
{
Name: "EE - Evil Empire",
Name: "EE - Evil Empire",
DefaultRole: roles.MemberRoleName,
Public: true,
},
{
Name: "The Good Place",
Name: "The Good Place",
DefaultRole: roles.EditorRoleName,
Public: true,
},
},
},
@ -187,12 +191,17 @@ func TestOrganizationsStore_All(t *testing.T) {
{
Name: bolt.DefaultOrganizationName,
DefaultRole: bolt.DefaultOrganizationRole,
Public: bolt.DefaultOrganizationPublic,
},
{
Name: "EE - Evil Empire",
Name: "EE - Evil Empire",
DefaultRole: roles.MemberRoleName,
Public: true,
},
{
Name: "The Good Place",
Name: "The Good Place",
DefaultRole: roles.EditorRoleName,
Public: true,
},
},
addFirst: true,
@ -238,10 +247,9 @@ func TestOrganizationsStore_Update(t *testing.T) {
orgs []chronograf.Organization
}
type args struct {
ctx context.Context
org *chronograf.Organization
name string
defaultRole string
ctx context.Context
initial *chronograf.Organization
updates *chronograf.Organization
}
tests := []struct {
name string
@ -256,10 +264,11 @@ func TestOrganizationsStore_Update(t *testing.T) {
fields: fields{},
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
initial: &chronograf.Organization{
ID: 1234,
Name: "The Okay Place",
},
updates: &chronograf.Organization{},
},
wantErr: true,
},
@ -268,10 +277,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
fields: fields{},
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
initial: &chronograf.Organization{
Name: "The Good Place",
},
name: "The Bad Place",
updates: &chronograf.Organization{
Name: "The Bad Place",
},
},
want: &chronograf.Organization{
Name: "The Bad Place",
@ -283,10 +294,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
fields: fields{},
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
initial: &chronograf.Organization{
Name: "The Good Place",
},
defaultRole: roles.ViewerRoleName,
updates: &chronograf.Organization{
DefaultRole: roles.ViewerRoleName,
},
},
want: &chronograf.Organization{
Name: "The Good Place",
@ -299,12 +312,14 @@ func TestOrganizationsStore_Update(t *testing.T) {
fields: fields{},
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
initial: &chronograf.Organization{
Name: "The Good Place",
DefaultRole: roles.AdminRoleName,
},
name: "The Bad Place",
defaultRole: roles.ViewerRoleName,
updates: &chronograf.Organization{
Name: "The Bad Place",
DefaultRole: roles.ViewerRoleName,
},
},
want: &chronograf.Organization{
Name: "The Bad Place",
@ -312,6 +327,51 @@ func TestOrganizationsStore_Update(t *testing.T) {
},
addFirst: true,
},
{
name: "Update organization name, role, public",
fields: fields{},
args: args{
ctx: context.Background(),
initial: &chronograf.Organization{
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: false,
},
updates: &chronograf.Organization{
Name: "The Bad Place",
Public: true,
DefaultRole: roles.AdminRoleName,
},
},
want: &chronograf.Organization{
Name: "The Bad Place",
Public: true,
DefaultRole: roles.AdminRoleName,
},
addFirst: true,
},
{
name: "Update organization name and public",
fields: fields{},
args: args{
ctx: context.Background(),
initial: &chronograf.Organization{
Name: "The Good Place",
DefaultRole: roles.EditorRoleName,
Public: false,
},
updates: &chronograf.Organization{
Name: "The Bad Place",
Public: true,
},
},
want: &chronograf.Organization{
Name: "The Bad Place",
DefaultRole: roles.EditorRoleName,
Public: true,
},
addFirst: true,
},
{
name: "Update organization name - name already taken",
fields: fields{
@ -323,10 +383,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
},
args: args{
ctx: context.Background(),
org: &chronograf.Organization{
initial: &chronograf.Organization{
Name: "The Good Place",
},
name: "The Bad Place",
updates: &chronograf.Organization{
Name: "The Bad Place",
},
},
wantErr: true,
addFirst: true,
@ -351,17 +413,21 @@ func TestOrganizationsStore_Update(t *testing.T) {
}
if tt.addFirst {
tt.args.org, err = s.Add(tt.args.ctx, tt.args.org)
tt.args.initial, err = s.Add(tt.args.ctx, tt.args.initial)
}
if tt.args.name != "" {
tt.args.org.Name = tt.args.name
if tt.args.updates.Name != "" {
tt.args.initial.Name = tt.args.updates.Name
}
if tt.args.defaultRole != "" {
tt.args.org.DefaultRole = tt.args.defaultRole
if tt.args.updates.DefaultRole != "" {
tt.args.initial.DefaultRole = tt.args.updates.DefaultRole
}
if err := s.Update(tt.args.ctx, tt.args.org); (err != nil) != tt.wantErr {
if tt.args.updates.Public != tt.args.initial.Public {
tt.args.initial.Public = tt.args.updates.Public
}
if err := s.Update(tt.args.ctx, tt.args.initial); (err != nil) != tt.wantErr {
t.Errorf("%q. OrganizationsStore.Update() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
@ -370,7 +436,7 @@ func TestOrganizationsStore_Update(t *testing.T) {
continue
}
got, err := s.Get(tt.args.ctx, chronograf.OrganizationQuery{Name: &tt.args.org.Name})
got, err := s.Get(tt.args.ctx, chronograf.OrganizationQuery{Name: &tt.args.initial.Name})
if err != nil {
t.Fatalf("failed to get organization: %v", err)
}
@ -572,6 +638,7 @@ func TestOrganizationsStore_DefaultOrganization(t *testing.T) {
ID: bolt.DefaultOrganizationID,
Name: bolt.DefaultOrganizationName,
DefaultRole: bolt.DefaultOrganizationRole,
Public: bolt.DefaultOrganizationPublic,
},
wantErr: false,
},

View File

@ -786,6 +786,9 @@ type Organization struct {
Name string `json:"name"`
// DefaultRole is the name of the role that is the default for any users added to the organization
DefaultRole string `json:"defaultRole,omitempty"`
// Public specifies whether users must be explicitly added to the organization.
// It is currently only used by the default organization, but that may change in the future.
Public bool `json:"public"`
}
// OrganizationQuery represents the attributes that a organization may be retrieved by.

View File

@ -11,7 +11,6 @@ import (
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/oauth2"
"github.com/influxdata/chronograf/organizations"
"github.com/influxdata/chronograf/roles"
)
type meLinks struct {
@ -72,14 +71,14 @@ func getValidPrincipal(ctx context.Context) (oauth2.Principal, error) {
return p, nil
}
type meOrganizationRequest struct {
type meRequest struct {
// Organization is the OrganizationID
Organization string `json:"organization"`
}
// MeOrganization changes the user's current organization on the JWT and responds
// UpdateMe changes the user's current organization on the JWT and responds
// with the same semantics as Me
func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWriter, *http.Request) {
func (s *Service) UpdateMe(auth oauth2.Authenticator) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
serverCtx := serverContext(ctx)
@ -89,7 +88,7 @@ func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWr
Error(w, http.StatusForbidden, "invalid principal", s.Logger)
return
}
var req meOrganizationRequest
var req meRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
invalidJSON(w, s.Logger)
return
@ -134,7 +133,8 @@ func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWr
Scheme: &scheme,
})
if err == chronograf.ErrUserNotFound {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
// Since a user is not a part of this organization, we should tell them that they are Forbidden (403) from accessing this resource
Error(w, http.StatusForbidden, err.Error(), s.Logger)
return
}
if err != nil {
@ -199,7 +199,18 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
return
}
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
if usr != nil {
// If the default org is private and the user has no roles, they should not have access
if !defaultOrg.Public && len(usr.Roles) == 0 {
Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
return
}
orgID, err := parseOrganizationID(p.Organization)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
@ -210,14 +221,13 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
unknownErrorWithMessage(w, err, s.Logger)
return
}
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
// If a user was added via the API, they might not yet be a member of the default organization
// Here we check to verify that they are a user in the default organization
// TODO(desa): when https://github.com/influxdata/chronograf/pull/2219 is merge, refactor this to use
// the default organization logic rather than hard coding valies here.
if !hasRoleInDefaultOrganization(usr) {
if !hasRoleInDefaultOrganization(usr, defaultOrgID) {
usr.Roles = append(usr.Roles, chronograf.Role{
Organization: "0",
Name: roles.MemberRoleName,
Organization: defaultOrgID,
Name: defaultOrg.DefaultRole,
})
if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
unknownErrorWithMessage(w, err, s.Logger)
@ -236,9 +246,10 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
return
}
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
// If users must be explicitly added to the default organization, respond with 403
// forbidden
if !defaultOrg.Public {
Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
return
}
@ -252,7 +263,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
Scheme: scheme,
Roles: []chronograf.Role{
{
Name: roles.MemberRoleName,
Name: defaultOrg.DefaultRole,
// This is the ID of the default organization
Organization: fmt.Sprintf("%d", defaultOrg.ID),
},
@ -327,11 +338,9 @@ func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([
return orgs, nil
}
// TODO(desa): when https://github.com/influxdata/chronograf/pull/2219 is merge, refactor this to use
// the default organization logic rather than hard coding valies here.
func hasRoleInDefaultOrganization(u *chronograf.User) bool {
func hasRoleInDefaultOrganization(u *chronograf.User, orgID string) bool {
for _, role := range u.Roles {
if role.Organization == "0" {
if role.Organization == orgID {
return true
}
}

View File

@ -39,6 +39,71 @@ func TestService_Me(t *testing.T) {
wantContentType string
wantBody string
}{
{
name: "Existing user - not member of any organization",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: false,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: false,
}, nil
case 1:
return &chronograf.Organization{
ID: 1,
Name: "The Bad Place",
Public: false,
}, nil
}
return nil, nil
},
},
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
// This function gets to verify that there is at least one first user
return []chronograf.User{{}}, nil
},
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
if q.Name == nil || q.Provider == nil || q.Scheme == nil {
return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme")
}
return &chronograf.User{
Name: "me",
Provider: "github",
Scheme: "oauth2",
}, nil
},
UpdateF: func(ctx context.Context, u *chronograf.User) error {
return nil
},
},
},
principal: oauth2.Principal{
Subject: "me",
Issuer: "github",
},
wantStatus: http.StatusForbidden,
wantContentType: "application/json",
wantBody: `{"code":403,"message":"This organization is private. To gain access, you must be explicitly added by an administrator."}`,
},
{
name: "Existing user",
args: args{
@ -51,21 +116,26 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "Default",
ID: 0,
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
switch *q.ID {
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
ID: 0,
Name: "Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
case 1:
return &chronograf.Organization{
ID: 1,
Name: "The Bad Place",
ID: 1,
Name: "The Bad Place",
Public: true,
}, nil
}
return nil, nil
@ -97,11 +167,11 @@ func TestService_Me(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"me","roles":[{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"}],"currentOrganization":{"id":"0","name":"Default"}}
wantBody: `{"name":"me","roles":[{"name":"viewer","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default","public":true,"defaultRole":"viewer"}],"currentOrganization":{"id":"0","defaultRole":"viewer","name":"Default","public":true}}
`,
},
{
name: "New user",
name: "new user - default org is public",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
@ -112,13 +182,18 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: 0,
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "The Bad Place",
ID: 0,
Name: "The Gnarly Default",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
},
},
@ -147,7 +222,7 @@ func TestService_Me(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"secret","roles":[{"name":"member","organization":"0"}],"provider":"auth0","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"The Bad Place"}],"currentOrganization":{"id":"0","name":"The Bad Place"}}
wantBody: `{"name":"secret","roles":[{"name":"viewer","organization":"0"}],"provider":"auth0","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"The Gnarly Default","public":true,"defaultRole":"viewer"}],"currentOrganization":{"id":"0","name":"The Gnarly Default","public":true,"defaultRole":"viewer"}}
`,
},
{
@ -161,13 +236,15 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: 0,
Public: true,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "The Bad Place",
ID: 0,
Name: "The Bad Place",
Public: true,
}, nil
},
},
@ -227,6 +304,52 @@ func TestService_Me(t *testing.T) {
Issuer: "",
},
},
{
name: "new user - default org is private",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "The Bad Place",
DefaultRole: roles.MemberRoleName,
Public: false,
}, nil
},
},
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
// This function gets to verify that there is at least one first user
return []chronograf.User{{}}, nil
},
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
if q.Name == nil || q.Provider == nil || q.Scheme == nil {
return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme")
}
return nil, chronograf.ErrUserNotFound
},
AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
return u, nil
},
UpdateF: func(ctx context.Context, u *chronograf.User) error {
return nil
},
},
},
principal: oauth2.Principal{
Subject: "secret",
Issuer: "auth0",
},
wantStatus: http.StatusForbidden,
wantContentType: "application/json",
wantBody: `{"code":403,"message":"This organization is private. To gain access, you must be explicitly added by an administrator."}`,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(context.WithValue(context.Background(), oauth2.PrincipalKey, tt.principal))
@ -260,7 +383,7 @@ func TestService_Me(t *testing.T) {
}
}
func TestService_MeOrganizations(t *testing.T) {
func TestService_UpdateMe(t *testing.T) {
type fields struct {
UsersStore chronograf.UsersStore
OrganizationsStore chronograf.OrganizationsStore
@ -268,10 +391,10 @@ func TestService_MeOrganizations(t *testing.T) {
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
orgRequest *meOrganizationRequest
auth mocks.Authenticator
w *httptest.ResponseRecorder
r *http.Request
meRequest *meRequest
auth mocks.Authenticator
}
tests := []struct {
name string
@ -287,7 +410,7 @@ func TestService_MeOrganizations(t *testing.T) {
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
orgRequest: &meOrganizationRequest{
meRequest: &meRequest{
Organization: "1337",
},
auth: mocks.Authenticator{},
@ -319,8 +442,10 @@ func TestService_MeOrganizations(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "Default",
ID: 0,
Name: "Default",
DefaultRole: roles.AdminRoleName,
Public: true,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -330,13 +455,16 @@ func TestService_MeOrganizations(t *testing.T) {
switch *q.ID {
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
ID: 0,
Name: "Default",
DefaultRole: roles.AdminRoleName,
Public: true,
}, nil
case 1337:
return &chronograf.Organization{
ID: 1337,
Name: "The ShillBillThrilliettas",
ID: 1337,
Name: "The ShillBillThrilliettas",
Public: true,
}, nil
}
return nil, nil
@ -349,14 +477,14 @@ func TestService_MeOrganizations(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"},{"id":"1337","name":"The ShillBillThrilliettas"}],"currentOrganization":{"id":"1337","name":"The ShillBillThrilliettas"}}`,
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"admin","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default","public":true,"defaultRole":"admin"},{"id":"1337","name":"The ShillBillThrilliettas","public":true}],"currentOrganization":{"id":"1337","name":"The ShillBillThrilliettas","public":true}}`,
},
{
name: "Change the current User's organization",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
orgRequest: &meOrganizationRequest{
meRequest: &meRequest{
Organization: "1337",
},
auth: mocks.Authenticator{},
@ -388,8 +516,10 @@ func TestService_MeOrganizations(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "Default",
ID: 0,
Name: "Default",
DefaultRole: roles.EditorRoleName,
Public: true,
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
@ -399,13 +529,16 @@ func TestService_MeOrganizations(t *testing.T) {
switch *q.ID {
case 1337:
return &chronograf.Organization{
ID: 1337,
Name: "The ThrillShilliettos",
ID: 1337,
Name: "The ThrillShilliettos",
Public: false,
}, nil
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
ID: 0,
Name: "Default",
DefaultRole: roles.EditorRoleName,
Public: true,
}, nil
}
return nil, nil
@ -419,14 +552,14 @@ func TestService_MeOrganizations(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"},{"id":"1337","name":"The ThrillShilliettos"}],"currentOrganization":{"id":"1337","name":"The ThrillShilliettos"}}`,
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"editor","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default","public":true,"defaultRole":"editor"},{"id":"1337","name":"The ThrillShilliettos","public":false}],"currentOrganization":{"id":"1337","name":"The ThrillShilliettos","public":false}}`,
},
{
name: "Unable to find requested user in valid organization",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
orgRequest: &meOrganizationRequest{
meRequest: &meRequest{
Organization: "1337",
},
auth: mocks.Authenticator{},
@ -466,8 +599,9 @@ func TestService_MeOrganizations(t *testing.T) {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
Name: "The ShillBillThrilliettas",
ID: 1337,
Name: "The ShillBillThrilliettas",
Public: true,
}, nil
},
},
@ -477,16 +611,16 @@ func TestService_MeOrganizations(t *testing.T) {
Issuer: "github",
Organization: "1338",
},
wantStatus: http.StatusBadRequest,
wantStatus: http.StatusForbidden,
wantContentType: "application/json",
wantBody: `{"code":400,"message":"user not found"}`,
wantBody: `{"code":403,"message":"user not found"}`,
},
{
name: "Unable to find requested organization",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
orgRequest: &meOrganizationRequest{
meRequest: &meRequest{
Organization: "1337",
},
auth: mocks.Authenticator{},
@ -547,24 +681,24 @@ func TestService_MeOrganizations(t *testing.T) {
UseAuth: tt.fields.UseAuth,
}
buf, _ := json.Marshal(tt.args.orgRequest)
buf, _ := json.Marshal(tt.args.meRequest)
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
tt.args.auth.Principal = tt.principal
s.MeOrganization(&tt.args.auth)(tt.args.w, tt.args.r)
s.UpdateMe(&tt.args.auth)(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. Me() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
t.Errorf("%q. UpdateMe() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. Me() = %v, want %v", tt.name, content, tt.wantContentType)
t.Errorf("%q. UpdateMe() = %v, want %v", tt.name, content, tt.wantContentType)
}
if eq, err := jsonEqual(tt.wantBody, string(body)); err != nil || !eq {
t.Errorf("%q. Me() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
t.Errorf("%q. UpdateMe() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}

View File

@ -198,7 +198,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
router.GET("/chronograf/v1/me", service.Me)
// Set current chronograf organization the user is logged into
router.PUT("/chronograf/v1/me", service.MeOrganization(opts.Auth))
router.PUT("/chronograf/v1/me", service.UpdateMe(opts.Auth))
// TODO(desa): what to do about admin's being able to set superadmin
router.GET("/chronograf/v1/users", EnsureAdmin(service.Users))

View File

@ -20,6 +20,7 @@ func parseOrganizationID(id string) (uint64, error) {
type organizationRequest struct {
Name string `json:"name"`
DefaultRole string `json:"defaultRole"`
Public *bool `json:"public"`
}
func (r *organizationRequest) ValidCreate() error {
@ -31,7 +32,7 @@ func (r *organizationRequest) ValidCreate() error {
}
func (r *organizationRequest) ValidUpdate() error {
if r.Name == "" && r.DefaultRole == "" {
if r.Name == "" && r.DefaultRole == "" && r.Public == nil {
return fmt.Errorf("No fields to update")
}
@ -56,17 +57,16 @@ func (r *organizationRequest) ValidDefaultRole() error {
}
type organizationResponse struct {
Links selfLinks `json:"links"`
ID uint64 `json:"id,string"`
Name string `json:"name"`
DefaultRole string `json:"defaultRole,omitempty"`
Links selfLinks `json:"links"`
chronograf.Organization
}
func newOrganizationResponse(o *chronograf.Organization) *organizationResponse {
if o == nil {
o = &chronograf.Organization{}
}
return &organizationResponse{
ID: o.ID,
Name: o.Name,
DefaultRole: o.DefaultRole,
Organization: *o,
Links: selfLinks{
Self: fmt.Sprintf("/chronograf/v1/organizations/%d", o.ID),
},
@ -124,6 +124,10 @@ func (s *Service) NewOrganization(w http.ResponseWriter, r *http.Request) {
DefaultRole: req.DefaultRole,
}
if req.Public != nil {
org.Public = *req.Public
}
res, err := s.Store.Organizations(ctx).Add(ctx, org)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
@ -219,6 +223,10 @@ func (s *Service) UpdateOrganization(w http.ResponseWriter, r *http.Request) {
org.DefaultRole = req.DefaultRole
}
if req.Public != nil {
org.Public = *req.Public
}
err = s.Store.Organizations(ctx).Update(ctx, org)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), s.Logger)

View File

@ -52,8 +52,9 @@ func TestService_OrganizationID(t *testing.T) {
switch *q.ID {
case 1337:
return &chronograf.Organization{
ID: 1337,
Name: "The Good Place",
ID: 1337,
Name: "The Good Place",
Public: false,
}, nil
default:
return nil, fmt.Errorf("Organization with ID %s not found", *q.ID)
@ -64,7 +65,7 @@ func TestService_OrganizationID(t *testing.T) {
id: "1337",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
wantBody: `{"links":{"self":"/chronograf/v1/organizations/1337"},"id":"1337","name":"The Good Place","public":false}`,
},
}
@ -138,12 +139,14 @@ func TestService_Organizations(t *testing.T) {
AllF: func(ctx context.Context) ([]chronograf.Organization, error) {
return []chronograf.Organization{
chronograf.Organization{
ID: 1337,
Name: "The Good Place",
ID: 1337,
Name: "The Good Place",
Public: false,
},
chronograf.Organization{
ID: 100,
Name: "The Bad Place",
ID: 100,
Name: "The Bad Place",
Public: false,
},
}, nil
},
@ -151,7 +154,7 @@ func TestService_Organizations(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"organizations":[{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}},{"id":"100","name":"The Bad Place","links":{"self":"/chronograf/v1/organizations/100"}}],"links":{"self":"/chronograf/v1/organizations"}}`,
wantBody: `{"links":{"self":"/chronograf/v1/organizations"},"organizations":[{"links":{"self":"/chronograf/v1/organizations/1337"},"id":"1337","name":"The Good Place","public":false},{"links":{"self":"/chronograf/v1/organizations/100"},"id":"100","name":"The Bad Place","public":false}]}`,
},
}
@ -189,9 +192,11 @@ func TestService_UpdateOrganization(t *testing.T) {
Logger chronograf.Logger
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
org *organizationRequest
w *httptest.ResponseRecorder
r *http.Request
org *organizationRequest
public bool
setPtr bool
}
tests := []struct {
name string
@ -223,8 +228,10 @@ func TestService_UpdateOrganization(t *testing.T) {
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
Name: "The Good Place",
ID: 1337,
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: false,
}, nil
},
},
@ -232,7 +239,73 @@ func TestService_UpdateOrganization(t *testing.T) {
id: "1337",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"id":"1337","name":"The Bad Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
wantBody: `{"id":"1337","name":"The Bad Place","defaultRole":"viewer","links":{"self":"/chronograf/v1/organizations/1337"},"public":false}`,
},
{
name: "Update Organization public",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"GET",
"http://any.url", // can be any valid URL as we are bypassing mux
nil,
),
org: &organizationRequest{},
public: false,
setPtr: true,
},
fields: fields{
Logger: log.New(log.DebugLevel),
OrganizationsStore: &mocks.OrganizationsStore{
UpdateF: func(ctx context.Context, o *chronograf.Organization) error {
return nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
},
},
},
id: "0",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"id":"0","name":"The Good Place","defaultRole":"viewer","public":false,"links":{"self":"/chronograf/v1/organizations/0"}}`,
},
{
name: "Update Organization - nothing to update",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"GET",
"http://any.url", // can be any valid URL as we are bypassing mux
nil,
),
org: &organizationRequest{},
},
fields: fields{
Logger: log.New(log.DebugLevel),
OrganizationsStore: &mocks.OrganizationsStore{
UpdateF: func(ctx context.Context, o *chronograf.Organization) error {
return nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
Name: "The Good Place",
DefaultRole: roles.ViewerRoleName,
Public: true,
}, nil
},
},
},
id: "1337",
wantStatus: http.StatusUnprocessableEntity,
wantContentType: "application/json",
wantBody: `{"code":422,"message":"No fields to update"}`,
},
{
name: "Update Organization default role",
@ -258,6 +331,7 @@ func TestService_UpdateOrganization(t *testing.T) {
ID: 1337,
Name: "The Good Place",
DefaultRole: roles.MemberRoleName,
Public: false,
}, nil
},
},
@ -265,7 +339,7 @@ func TestService_UpdateOrganization(t *testing.T) {
id: "1337",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"id":"1337","name":"The Good Place","defaultRole":"viewer","links":{"self":"/chronograf/v1/organizations/1337"}}`,
wantBody: `{"links":{"self":"/chronograf/v1/organizations/1337"},"id":"1337","name":"The Good Place","defaultRole":"viewer","public":false}`,
},
{
name: "Update Organization - invalid update",
@ -341,6 +415,11 @@ func TestService_UpdateOrganization(t *testing.T) {
Value: tt.id,
},
}))
if tt.args.setPtr {
tt.args.org.Public = &tt.args.public
}
buf, _ := json.Marshal(tt.args.org)
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
s.UpdateOrganization(tt.args.w, tt.args.r)
@ -494,15 +573,16 @@ func TestService_NewOrganization(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 1337,
Name: "The Good Place",
ID: 1337,
Name: "The Good Place",
Public: false,
}, nil
},
},
},
wantStatus: http.StatusCreated,
wantContentType: "application/json",
wantBody: `{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
wantBody: `{"id":"1337","public":false,"name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
},
{
name: "Create Organization - no user on context",

View File

@ -5,8 +5,10 @@ import uuid from 'node-uuid'
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
import OrganizationsTableRowDefault from 'src/admin/components/chronograf/OrganizationsTableRowDefault'
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
import {DEFAULT_ORG_ID} from 'src/admin/constants/dummyUsers'
import {PUBLIC_TOOLTIP} from 'src/admin/constants/index'
class OrganizationsTable extends Component {
constructor(props) {
@ -16,6 +18,7 @@ class OrganizationsTable extends Component {
isCreatingOrganization: false,
}
}
handleClickCreateOrganization = () => {
this.setState({isCreatingOrganization: true})
}
@ -36,6 +39,7 @@ class OrganizationsTable extends Component {
onDeleteOrg,
onRenameOrg,
onChooseDefaultRole,
onTogglePublic,
} = this.props
const {isCreatingOrganization} = this.state
@ -62,6 +66,10 @@ class OrganizationsTable extends Component {
<div className="orgs-table--org-labels">
<div className="orgs-table--id">ID</div>
<div className="orgs-table--name">Name</div>
<div className="orgs-table--public">
Public{' '}
<QuestionMarkTooltip tipID="public" tipContent={PUBLIC_TOOLTIP} />
</div>
<div className="orgs-table--default-role">Default Role</div>
<div className="orgs-table--delete" />
</div>
@ -77,6 +85,7 @@ class OrganizationsTable extends Component {
? <OrganizationsTableRowDefault
key={uuid.v4()}
organization={org}
onTogglePublic={onTogglePublic}
onChooseDefaultRole={onChooseDefaultRole}
/>
: <OrganizationsTableRow
@ -105,6 +114,7 @@ OrganizationsTable.propTypes = {
onCreateOrg: func.isRequired,
onDeleteOrg: func.isRequired,
onRenameOrg: func.isRequired,
onTogglePublic: func.isRequired,
onChooseDefaultRole: func.isRequired,
}
export default OrganizationsTable

View File

@ -117,6 +117,7 @@ class OrganizationsTableRow extends Component {
{workingName}
<span className="icon pencil" />
</div>}
<div className="orgs-table--public disabled">&mdash;</div>
<div className={defaultRoleClassName}>
<Dropdown
items={dropdownRolesItems}

View File

@ -1,11 +1,17 @@
import React, {PropTypes, Component} from 'react'
import SlideToggle from 'shared/components/SlideToggle'
import Dropdown from 'shared/components/Dropdown'
import {USER_ROLES} from 'src/admin/constants/dummyUsers'
// This is a non-editable organization row, used currently for DEFAULT_ORG
class OrganizationsTableRowDefault extends Component {
togglePublic = () => {
const {organization, onTogglePublic} = this.props
onTogglePublic(organization)
}
handleChooseDefaultRole = role => {
const {organization, onChooseDefaultRole} = this.props
onChooseDefaultRole(organization, role.name)
@ -27,6 +33,13 @@ class OrganizationsTableRowDefault extends Component {
<div className="orgs-table--name-disabled">
{organization.name}
</div>
<div className="orgs-table--public">
<SlideToggle
size="xs"
active={organization.public}
onToggle={this.togglePublic}
/>
</div>
<div className="orgs-table--default-role">
<Dropdown
items={dropdownRolesItems}
@ -53,6 +66,7 @@ OrganizationsTableRowDefault.propTypes = {
id: string,
name: string.isRequired,
}).isRequired,
onTogglePublic: func.isRequired,
onChooseDefaultRole: func.isRequired,
}

View File

@ -46,3 +46,6 @@ export const NEW_DEFAULT_DATABASE = {
isNew: true,
retentionPolicies: [NEW_DEFAULT_RP],
}
export const PUBLIC_TOOLTIP =
'If set to <code>false</code>, users cannot<br/>authenticate unless an <strong>Admin</strong> explicitly<br/>adds them to the organization.'

View File

@ -29,6 +29,14 @@ class OrganizationsPage extends Component {
deleteOrganizationAsync(organization)
}
handleTogglePublic = organization => {
const {actions: {updateOrganizationAsync}} = this.props
updateOrganizationAsync(organization, {
...organization,
public: !organization.public,
})
}
handleChooseDefaultRole = (organization, defaultRole) => {
const {actions: {updateOrganizationAsync}} = this.props
updateOrganizationAsync(organization, {...organization, defaultRole})
@ -43,6 +51,7 @@ class OrganizationsPage extends Component {
onCreateOrg={this.handleCreateOrganization}
onDeleteOrg={this.handleDeleteOrganization}
onRenameOrg={this.handleRenameOrganization}
onTogglePublic={this.handleTogglePublic}
onChooseDefaultRole={this.handleChooseDefaultRole}
/>
)

View File

@ -47,9 +47,7 @@ input[type="text"].form-control.orgs-table--input {
background-color: $g2-kevlar;
color: $g13-mist;
position: relative;
transition:
color 0.4s ease,
background-color 0.4s ease,
transition: color 0.4s ease, background-color 0.4s ease,
border-color 0.4s ease;
> span.icon {
@ -68,7 +66,9 @@ input[type="text"].form-control.orgs-table--input {
border-color: $g5-pepper;
cursor: text;
> span.icon {opacity: 1;}
> span.icon {
opacity: 1;
}
}
}
.orgs-table--name-disabled {
@ -78,6 +78,29 @@ input[type="text"].form-control.orgs-table--input {
color: $g9-mountain;
}
.orgs-table--public {
height: 30px;
margin-right: 4px;
text-align: center;
width: 104px;
background-color: $g4-onyx;
border-radius: 4px;
line-height: 30px;
position: relative;
> .slide-toggle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&.disabled {
color: $g9-mountain;
@include no-user-select();
}
}
.orgs-table--default-role,
.orgs-table--default-role-disabled {
width: 130px;
@ -102,7 +125,6 @@ input[type="text"].form-control.orgs-table--input {
width: 30px;
}
/* Table Headers */
.orgs-table--org-labels {
display: flex;
@ -113,7 +135,8 @@ input[type="text"].form-control.orgs-table--input {
@include no-user-select();
> .orgs-table--name,
> .orgs-table--name:hover {
> .orgs-table--name:hover,
> .orgs-table--public {
transition: none;
background-color: transparent;
border-color: transparent;
@ -122,11 +145,13 @@ input[type="text"].form-control.orgs-table--input {
> .orgs-table--id,
> .orgs-table--name,
> .orgs-table--name:hover,
> .orgs-table--default-role {
> .orgs-table--default-role,
> .orgs-table--public {
color: $g17-whisper;
font-weight: 500;
}
> .orgs-table--default-role {
> .orgs-table--default-role,
> .orgs-table--public {
line-height: 30px;
font-size: 13px;
padding: 0 11px;