diff --git a/api/go/gosdn/rbac/rbac.pb.go b/api/go/gosdn/rbac/rbac.pb.go
index 72064841bcbf4a51ce00a0fd237370487c77c816..1752a165cb9c46c69269b95a8193636906b8796b 100644
--- a/api/go/gosdn/rbac/rbac.pb.go
+++ b/api/go/gosdn/rbac/rbac.pb.go
@@ -72,56 +72,156 @@ func (Status) EnumDescriptor() ([]byte, []int) {
 	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{0}
 }
 
-// TODO: add additional data to user enum
-type User int32
+type User struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
 
-const (
-	User_USER_UNSPECIFIED User = 0
-	User_USER_NAME        User = 1
-	User_USER_PWD         User = 2 // ...
-)
+	Id       string            `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	Name     string            `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Roles    map[string]string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Key = pnd uuid, value= role name
+	Password string            `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"`
+	Token    string            `protobuf:"bytes,5,opt,name=token,proto3" json:"token,omitempty"`
+}
 
-// Enum value maps for User.
-var (
-	User_name = map[int32]string{
-		0: "USER_UNSPECIFIED",
-		1: "USER_NAME",
-		2: "USER_PWD",
+func (x *User) Reset() {
+	*x = User{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
 	}
-	User_value = map[string]int32{
-		"USER_UNSPECIFIED": 0,
-		"USER_NAME":        1,
-		"USER_PWD":         2,
+}
+
+func (x *User) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*User) ProtoMessage() {}
+
+func (x *User) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[0]
+	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)
+}
 
-func (x User) Enum() *User {
-	p := new(User)
-	*p = x
-	return p
+// Deprecated: Use User.ProtoReflect.Descriptor instead.
+func (*User) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{0}
 }
 
-func (x User) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+func (x *User) GetId() string {
+	if x != nil {
+		return x.Id
+	}
+	return ""
+}
+
+func (x *User) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
 }
 
-func (User) Descriptor() protoreflect.EnumDescriptor {
-	return file_gosdn_rbac_rbac_proto_enumTypes[1].Descriptor()
+func (x *User) GetRoles() map[string]string {
+	if x != nil {
+		return x.Roles
+	}
+	return nil
 }
 
-func (User) Type() protoreflect.EnumType {
-	return &file_gosdn_rbac_rbac_proto_enumTypes[1]
+func (x *User) GetPassword() string {
+	if x != nil {
+		return x.Password
+	}
+	return ""
 }
 
-func (x User) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
+func (x *User) GetToken() string {
+	if x != nil {
+		return x.Token
+	}
+	return ""
+}
+
+type Role struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id          string   `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	Name        string   `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Description string   `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
+	Permissions []string `protobuf:"bytes,4,rep,name=permissions,proto3" json:"permissions,omitempty"`
+}
+
+func (x *Role) Reset() {
+	*x = Role{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Role) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Role) ProtoMessage() {}
+
+func (x *Role) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[1]
+	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 User.Descriptor instead.
-func (User) EnumDescriptor() ([]byte, []int) {
+// Deprecated: Use Role.ProtoReflect.Descriptor instead.
+func (*Role) Descriptor() ([]byte, []int) {
 	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{1}
 }
 
+func (x *Role) GetId() string {
+	if x != nil {
+		return x.Id
+	}
+	return ""
+}
+
+func (x *Role) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *Role) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+func (x *Role) GetPermissions() []string {
+	if x != nil {
+		return x.Permissions
+	}
+	return nil
+}
+
 // Login
 type LoginRequest struct {
 	state         protoimpl.MessageState
@@ -136,7 +236,7 @@ type LoginRequest struct {
 func (x *LoginRequest) Reset() {
 	*x = LoginRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[0]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -149,7 +249,7 @@ func (x *LoginRequest) String() string {
 func (*LoginRequest) ProtoMessage() {}
 
 func (x *LoginRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[0]
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -162,7 +262,7 @@ func (x *LoginRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead.
 func (*LoginRequest) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{0}
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *LoginRequest) GetTimestamp() int64 {
@@ -199,7 +299,7 @@ type LoginResponse struct {
 func (x *LoginResponse) Reset() {
 	*x = LoginResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[1]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -212,7 +312,7 @@ func (x *LoginResponse) String() string {
 func (*LoginResponse) ProtoMessage() {}
 
 func (x *LoginResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[1]
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -225,7 +325,7 @@ func (x *LoginResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead.
 func (*LoginResponse) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{1}
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *LoginResponse) GetTimestamp() int64 {
@@ -262,7 +362,7 @@ type LogoutRequest struct {
 func (x *LogoutRequest) Reset() {
 	*x = LogoutRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[2]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -275,7 +375,7 @@ func (x *LogoutRequest) String() string {
 func (*LogoutRequest) ProtoMessage() {}
 
 func (x *LogoutRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[2]
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -288,7 +388,7 @@ func (x *LogoutRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use LogoutRequest.ProtoReflect.Descriptor instead.
 func (*LogoutRequest) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{2}
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *LogoutRequest) GetTimestamp() int64 {
@@ -317,7 +417,7 @@ type LogoutResponse struct {
 func (x *LogoutResponse) Reset() {
 	*x = LogoutResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[3]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -330,7 +430,7 @@ func (x *LogoutResponse) String() string {
 func (*LogoutResponse) ProtoMessage() {}
 
 func (x *LogoutResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[3]
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -343,7 +443,7 @@ func (x *LogoutResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use LogoutResponse.ProtoReflect.Descriptor instead.
 func (*LogoutResponse) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{3}
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *LogoutResponse) GetTimestamp() int64 {
@@ -366,14 +466,14 @@ type CreateUsersRequest struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	User      []User `protobuf:"varint,2,rep,packed,name=user,proto3,enum=gosdn.rbac.User" json:"user,omitempty"`
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	User      []*User `protobuf:"bytes,2,rep,name=user,proto3" json:"user,omitempty"`
 }
 
 func (x *CreateUsersRequest) Reset() {
 	*x = CreateUsersRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[4]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -386,7 +486,7 @@ func (x *CreateUsersRequest) String() string {
 func (*CreateUsersRequest) ProtoMessage() {}
 
 func (x *CreateUsersRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[4]
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -399,7 +499,7 @@ func (x *CreateUsersRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateUsersRequest.ProtoReflect.Descriptor instead.
 func (*CreateUsersRequest) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{4}
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *CreateUsersRequest) GetTimestamp() int64 {
@@ -409,7 +509,7 @@ func (x *CreateUsersRequest) GetTimestamp() int64 {
 	return 0
 }
 
-func (x *CreateUsersRequest) GetUser() []User {
+func (x *CreateUsersRequest) GetUser() []*User {
 	if x != nil {
 		return x.User
 	}
@@ -428,7 +528,7 @@ type CreateUsersResponse struct {
 func (x *CreateUsersResponse) Reset() {
 	*x = CreateUsersResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[5]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -441,7 +541,7 @@ func (x *CreateUsersResponse) String() string {
 func (*CreateUsersResponse) ProtoMessage() {}
 
 func (x *CreateUsersResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[5]
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -454,7 +554,7 @@ func (x *CreateUsersResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateUsersResponse.ProtoReflect.Descriptor instead.
 func (*CreateUsersResponse) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{5}
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *CreateUsersResponse) GetTimestamp() int64 {
@@ -471,33 +571,33 @@ func (x *CreateUsersResponse) GetStatus() Status {
 	return Status_STATUS_UNSPECIFIED
 }
 
-// GetUsers
-type GetUsersRequest struct {
+// GetUser
+type GetUserRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
 	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	Token     string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
+	Name      string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
 }
 
-func (x *GetUsersRequest) Reset() {
-	*x = GetUsersRequest{}
+func (x *GetUserRequest) Reset() {
+	*x = GetUserRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[6]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *GetUsersRequest) String() string {
+func (x *GetUserRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*GetUsersRequest) ProtoMessage() {}
+func (*GetUserRequest) ProtoMessage() {}
 
-func (x *GetUsersRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[6]
+func (x *GetUserRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -508,52 +608,52 @@ func (x *GetUsersRequest) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use GetUsersRequest.ProtoReflect.Descriptor instead.
-func (*GetUsersRequest) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{6}
+// Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead.
+func (*GetUserRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{8}
 }
 
-func (x *GetUsersRequest) GetTimestamp() int64 {
+func (x *GetUserRequest) GetTimestamp() int64 {
 	if x != nil {
 		return x.Timestamp
 	}
 	return 0
 }
 
-func (x *GetUsersRequest) GetToken() string {
+func (x *GetUserRequest) GetName() string {
 	if x != nil {
-		return x.Token
+		return x.Name
 	}
 	return ""
 }
 
-type GetUsersResponse struct {
+type GetUserResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
 	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
 	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
-	User      []User `protobuf:"varint,3,rep,packed,name=user,proto3,enum=gosdn.rbac.User" json:"user,omitempty"`
+	User      *User  `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"`
 }
 
-func (x *GetUsersResponse) Reset() {
-	*x = GetUsersResponse{}
+func (x *GetUserResponse) Reset() {
+	*x = GetUserResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[7]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[9]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *GetUsersResponse) String() string {
+func (x *GetUserResponse) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*GetUsersResponse) ProtoMessage() {}
+func (*GetUserResponse) ProtoMessage() {}
 
-func (x *GetUsersResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[7]
+func (x *GetUserResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[9]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -564,59 +664,58 @@ func (x *GetUsersResponse) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use GetUsersResponse.ProtoReflect.Descriptor instead.
-func (*GetUsersResponse) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{7}
+// Deprecated: Use GetUserResponse.ProtoReflect.Descriptor instead.
+func (*GetUserResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{9}
 }
 
-func (x *GetUsersResponse) GetTimestamp() int64 {
+func (x *GetUserResponse) GetTimestamp() int64 {
 	if x != nil {
 		return x.Timestamp
 	}
 	return 0
 }
 
-func (x *GetUsersResponse) GetStatus() Status {
+func (x *GetUserResponse) GetStatus() Status {
 	if x != nil {
 		return x.Status
 	}
 	return Status_STATUS_UNSPECIFIED
 }
 
-func (x *GetUsersResponse) GetUser() []User {
+func (x *GetUserResponse) GetUser() *User {
 	if x != nil {
 		return x.User
 	}
 	return nil
 }
 
-// UpdateUsers
-type UpdateUsersRequest struct {
+// GetUsers
+type GetUsersRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	User      []User `protobuf:"varint,2,rep,packed,name=user,proto3,enum=gosdn.rbac.User" json:"user,omitempty"`
+	Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
 }
 
-func (x *UpdateUsersRequest) Reset() {
-	*x = UpdateUsersRequest{}
+func (x *GetUsersRequest) Reset() {
+	*x = GetUsersRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[8]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[10]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *UpdateUsersRequest) String() string {
+func (x *GetUsersRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*UpdateUsersRequest) ProtoMessage() {}
+func (*GetUsersRequest) ProtoMessage() {}
 
-func (x *UpdateUsersRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[8]
+func (x *GetUsersRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[10]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -627,51 +726,45 @@ func (x *UpdateUsersRequest) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use UpdateUsersRequest.ProtoReflect.Descriptor instead.
-func (*UpdateUsersRequest) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{8}
+// Deprecated: Use GetUsersRequest.ProtoReflect.Descriptor instead.
+func (*GetUsersRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{10}
 }
 
-func (x *UpdateUsersRequest) GetTimestamp() int64 {
+func (x *GetUsersRequest) GetTimestamp() int64 {
 	if x != nil {
 		return x.Timestamp
 	}
 	return 0
 }
 
-func (x *UpdateUsersRequest) GetUser() []User {
-	if x != nil {
-		return x.User
-	}
-	return nil
-}
-
-type UpdateUsersResponse struct {
+type GetUsersResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status  `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+	User      []*User `protobuf:"bytes,3,rep,name=user,proto3" json:"user,omitempty"`
 }
 
-func (x *UpdateUsersResponse) Reset() {
-	*x = UpdateUsersResponse{}
+func (x *GetUsersResponse) Reset() {
+	*x = GetUsersResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[9]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[11]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *UpdateUsersResponse) String() string {
+func (x *GetUsersResponse) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*UpdateUsersResponse) ProtoMessage() {}
+func (*GetUsersResponse) ProtoMessage() {}
 
-func (x *UpdateUsersResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[9]
+func (x *GetUsersResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[11]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -682,52 +775,59 @@ func (x *UpdateUsersResponse) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use UpdateUsersResponse.ProtoReflect.Descriptor instead.
-func (*UpdateUsersResponse) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{9}
+// Deprecated: Use GetUsersResponse.ProtoReflect.Descriptor instead.
+func (*GetUsersResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{11}
 }
 
-func (x *UpdateUsersResponse) GetTimestamp() int64 {
+func (x *GetUsersResponse) GetTimestamp() int64 {
 	if x != nil {
 		return x.Timestamp
 	}
 	return 0
 }
 
-func (x *UpdateUsersResponse) GetStatus() Status {
+func (x *GetUsersResponse) GetStatus() Status {
 	if x != nil {
 		return x.Status
 	}
 	return Status_STATUS_UNSPECIFIED
 }
 
-// DeleteUsers
-type DeleteUsersRequest struct {
+func (x *GetUsersResponse) GetUser() []*User {
+	if x != nil {
+		return x.User
+	}
+	return nil
+}
+
+// UpdateUsers
+type UpdateUsersRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Timestamp int64    `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	Username  []string `protobuf:"bytes,2,rep,name=username,proto3" json:"username,omitempty"`
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	User      []*User `protobuf:"bytes,2,rep,name=user,proto3" json:"user,omitempty"`
 }
 
-func (x *DeleteUsersRequest) Reset() {
-	*x = DeleteUsersRequest{}
+func (x *UpdateUsersRequest) Reset() {
+	*x = UpdateUsersRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[10]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[12]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *DeleteUsersRequest) String() string {
+func (x *UpdateUsersRequest) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*DeleteUsersRequest) ProtoMessage() {}
+func (*UpdateUsersRequest) ProtoMessage() {}
 
-func (x *DeleteUsersRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[10]
+func (x *UpdateUsersRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[12]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -738,26 +838,819 @@ func (x *DeleteUsersRequest) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use DeleteUsersRequest.ProtoReflect.Descriptor instead.
-func (*DeleteUsersRequest) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{10}
+// Deprecated: Use UpdateUsersRequest.ProtoReflect.Descriptor instead.
+func (*UpdateUsersRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *UpdateUsersRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *UpdateUsersRequest) GetUser() []*User {
+	if x != nil {
+		return x.User
+	}
+	return nil
+}
+
+type UpdateUsersResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+}
+
+func (x *UpdateUsersResponse) Reset() {
+	*x = UpdateUsersResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UpdateUsersResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UpdateUsersResponse) ProtoMessage() {}
+
+func (x *UpdateUsersResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[13]
+	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 UpdateUsersResponse.ProtoReflect.Descriptor instead.
+func (*UpdateUsersResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *UpdateUsersResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *UpdateUsersResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// DeleteUsers
+type DeleteUsersRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64    `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Username  []string `protobuf:"bytes,2,rep,name=username,proto3" json:"username,omitempty"`
+}
+
+func (x *DeleteUsersRequest) Reset() {
+	*x = DeleteUsersRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[14]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeleteUsersRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteUsersRequest) ProtoMessage() {}
+
+func (x *DeleteUsersRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[14]
+	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 DeleteUsersRequest.ProtoReflect.Descriptor instead.
+func (*DeleteUsersRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{14}
+}
+
+func (x *DeleteUsersRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *DeleteUsersRequest) GetUsername() []string {
+	if x != nil {
+		return x.Username
+	}
+	return nil
+}
+
+type DeleteUsersResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+}
+
+func (x *DeleteUsersResponse) Reset() {
+	*x = DeleteUsersResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[15]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeleteUsersResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteUsersResponse) ProtoMessage() {}
+
+func (x *DeleteUsersResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[15]
+	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 DeleteUsersResponse.ProtoReflect.Descriptor instead.
+func (*DeleteUsersResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{15}
+}
+
+func (x *DeleteUsersResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *DeleteUsersResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// CreateRoles
+type CreateRolesRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Roles     []*Role `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
+}
+
+func (x *CreateRolesRequest) Reset() {
+	*x = CreateRolesRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[16]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CreateRolesRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateRolesRequest) ProtoMessage() {}
+
+func (x *CreateRolesRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[16]
+	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 CreateRolesRequest.ProtoReflect.Descriptor instead.
+func (*CreateRolesRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{16}
+}
+
+func (x *CreateRolesRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *CreateRolesRequest) GetRoles() []*Role {
+	if x != nil {
+		return x.Roles
+	}
+	return nil
+}
+
+type CreateRolesResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+}
+
+func (x *CreateRolesResponse) Reset() {
+	*x = CreateRolesResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[17]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CreateRolesResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateRolesResponse) ProtoMessage() {}
+
+func (x *CreateRolesResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[17]
+	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 CreateRolesResponse.ProtoReflect.Descriptor instead.
+func (*CreateRolesResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{17}
+}
+
+func (x *CreateRolesResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *CreateRolesResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// GetRole
+type GetRoleRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	RoleName  string `protobuf:"bytes,2,opt,name=role_name,json=roleName,proto3" json:"role_name,omitempty"`
+}
+
+func (x *GetRoleRequest) Reset() {
+	*x = GetRoleRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[18]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetRoleRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetRoleRequest) ProtoMessage() {}
+
+func (x *GetRoleRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[18]
+	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 GetRoleRequest.ProtoReflect.Descriptor instead.
+func (*GetRoleRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *GetRoleRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetRoleRequest) GetRoleName() string {
+	if x != nil {
+		return x.RoleName
+	}
+	return ""
+}
+
+type GetRoleResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+	Role      *Role  `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"`
+}
+
+func (x *GetRoleResponse) Reset() {
+	*x = GetRoleResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[19]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetRoleResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetRoleResponse) ProtoMessage() {}
+
+func (x *GetRoleResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[19]
+	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 GetRoleResponse.ProtoReflect.Descriptor instead.
+func (*GetRoleResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{19}
+}
+
+func (x *GetRoleResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetRoleResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+func (x *GetRoleResponse) GetRole() *Role {
+	if x != nil {
+		return x.Role
+	}
+	return nil
+}
+
+// GetRoles
+type GetRolesRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+}
+
+func (x *GetRolesRequest) Reset() {
+	*x = GetRolesRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[20]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetRolesRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetRolesRequest) ProtoMessage() {}
+
+func (x *GetRolesRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[20]
+	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 GetRolesRequest.ProtoReflect.Descriptor instead.
+func (*GetRolesRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{20}
+}
+
+func (x *GetRolesRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+type GetRolesResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status  `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+	Roles     []*Role `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
+}
+
+func (x *GetRolesResponse) Reset() {
+	*x = GetRolesResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[21]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetRolesResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetRolesResponse) ProtoMessage() {}
+
+func (x *GetRolesResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[21]
+	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 GetRolesResponse.ProtoReflect.Descriptor instead.
+func (*GetRolesResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{21}
+}
+
+func (x *GetRolesResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetRolesResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+func (x *GetRolesResponse) GetRoles() []*Role {
+	if x != nil {
+		return x.Roles
+	}
+	return nil
+}
+
+// UpdateRoles
+type UpdateRolesRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Roles     []*Role `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
+}
+
+func (x *UpdateRolesRequest) Reset() {
+	*x = UpdateRolesRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[22]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UpdateRolesRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UpdateRolesRequest) ProtoMessage() {}
+
+func (x *UpdateRolesRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[22]
+	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 UpdateRolesRequest.ProtoReflect.Descriptor instead.
+func (*UpdateRolesRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{22}
+}
+
+func (x *UpdateRolesRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *UpdateRolesRequest) GetRoles() []*Role {
+	if x != nil {
+		return x.Roles
+	}
+	return nil
+}
+
+type UpdateRolesResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+}
+
+func (x *UpdateRolesResponse) Reset() {
+	*x = UpdateRolesResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[23]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UpdateRolesResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UpdateRolesResponse) ProtoMessage() {}
+
+func (x *UpdateRolesResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[23]
+	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 UpdateRolesResponse.ProtoReflect.Descriptor instead.
+func (*UpdateRolesResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{23}
+}
+
+func (x *UpdateRolesResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *UpdateRolesResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// DeletePermissionsForRole
+type DeletePermissionsForRoleRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp           int64    `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	RoleName            string   `protobuf:"bytes,2,opt,name=role_name,json=roleName,proto3" json:"role_name,omitempty"`
+	PermissionsToDelete []string `protobuf:"bytes,3,rep,name=permissions_to_delete,json=permissionsToDelete,proto3" json:"permissions_to_delete,omitempty"`
+}
+
+func (x *DeletePermissionsForRoleRequest) Reset() {
+	*x = DeletePermissionsForRoleRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[24]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeletePermissionsForRoleRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeletePermissionsForRoleRequest) ProtoMessage() {}
+
+func (x *DeletePermissionsForRoleRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[24]
+	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 DeletePermissionsForRoleRequest.ProtoReflect.Descriptor instead.
+func (*DeletePermissionsForRoleRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{24}
+}
+
+func (x *DeletePermissionsForRoleRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *DeletePermissionsForRoleRequest) GetRoleName() string {
+	if x != nil {
+		return x.RoleName
+	}
+	return ""
+}
+
+func (x *DeletePermissionsForRoleRequest) GetPermissionsToDelete() []string {
+	if x != nil {
+		return x.PermissionsToDelete
+	}
+	return nil
+}
+
+type DeletePermissionsForRoleResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
+}
+
+func (x *DeletePermissionsForRoleResponse) Reset() {
+	*x = DeletePermissionsForRoleResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[25]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeletePermissionsForRoleResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeletePermissionsForRoleResponse) ProtoMessage() {}
+
+func (x *DeletePermissionsForRoleResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[25]
+	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)
 }
 
-func (x *DeleteUsersRequest) GetTimestamp() int64 {
+// Deprecated: Use DeletePermissionsForRoleResponse.ProtoReflect.Descriptor instead.
+func (*DeletePermissionsForRoleResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{25}
+}
+
+func (x *DeletePermissionsForRoleResponse) GetTimestamp() int64 {
 	if x != nil {
 		return x.Timestamp
 	}
 	return 0
 }
 
-func (x *DeleteUsersRequest) GetUsername() []string {
+func (x *DeletePermissionsForRoleResponse) GetStatus() Status {
 	if x != nil {
-		return x.Username
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// DeleteRoles
+type DeleteRolesRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64    `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	RoleName  []string `protobuf:"bytes,2,rep,name=role_name,json=roleName,proto3" json:"role_name,omitempty"`
+}
+
+func (x *DeleteRolesRequest) Reset() {
+	*x = DeleteRolesRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[26]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeleteRolesRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteRolesRequest) ProtoMessage() {}
+
+func (x *DeleteRolesRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[26]
+	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 DeleteRolesRequest.ProtoReflect.Descriptor instead.
+func (*DeleteRolesRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{26}
+}
+
+func (x *DeleteRolesRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *DeleteRolesRequest) GetRoleName() []string {
+	if x != nil {
+		return x.RoleName
 	}
 	return nil
 }
 
-type DeleteUsersResponse struct {
+type DeleteRolesResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
@@ -766,23 +1659,23 @@ type DeleteUsersResponse struct {
 	Status    Status `protobuf:"varint,2,opt,name=status,proto3,enum=gosdn.rbac.Status" json:"status,omitempty"`
 }
 
-func (x *DeleteUsersResponse) Reset() {
-	*x = DeleteUsersResponse{}
+func (x *DeleteRolesResponse) Reset() {
+	*x = DeleteRolesResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_gosdn_rbac_rbac_proto_msgTypes[11]
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[27]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
 }
 
-func (x *DeleteUsersResponse) String() string {
+func (x *DeleteRolesResponse) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*DeleteUsersResponse) ProtoMessage() {}
+func (*DeleteRolesResponse) ProtoMessage() {}
 
-func (x *DeleteUsersResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_gosdn_rbac_rbac_proto_msgTypes[11]
+func (x *DeleteRolesResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[27]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -793,19 +1686,19 @@ func (x *DeleteUsersResponse) ProtoReflect() protoreflect.Message {
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use DeleteUsersResponse.ProtoReflect.Descriptor instead.
-func (*DeleteUsersResponse) Descriptor() ([]byte, []int) {
-	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{11}
+// Deprecated: Use DeleteRolesResponse.ProtoReflect.Descriptor instead.
+func (*DeleteRolesResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{27}
 }
 
-func (x *DeleteUsersResponse) GetTimestamp() int64 {
+func (x *DeleteRolesResponse) GetTimestamp() int64 {
 	if x != nil {
 		return x.Timestamp
 	}
 	return 0
 }
 
-func (x *DeleteUsersResponse) GetStatus() Status {
+func (x *DeleteRolesResponse) GetStatus() Status {
 	if x != nil {
 		return x.Status
 	}
@@ -824,126 +1717,274 @@ var file_gosdn_rbac_rbac_proto_rawDesc = []byte{
 	0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d,
 	0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
 	0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
-	0x70, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a,
-	0x03, 0x70, 0x77, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x77, 0x64, 0x22,
-	0x6f, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x6f, 0x74, 0x6f, 0x22, 0xc9, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02,
+	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x31, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x1b, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x73, 0x65,
+	0x72, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x72, 0x6f,
+	0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12,
+	0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+	0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0x38, 0x0a, 0x0a, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x45, 0x6e,
+	0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
+	0x6e, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a,
+	0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22,
+	0x5a, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a,
+	0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x77, 0x64,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x77, 0x64, 0x22, 0x6f, 0x0a, 0x0d, 0x4c,
+	0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
+	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73,
+	0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
+	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x49, 0x0a, 0x0d,
+	0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
+	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
+	0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x75,
+	0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75,
+	0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5a, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x6f, 0x75,
+	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d,
+	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e,
+	0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x22, 0x58, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65,
+	0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d,
+	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62,
+	0x61, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x5f, 0x0a,
+	0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+	0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+	0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e,
+	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x42,
+	0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
 	0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a,
-	0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12,
-	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f,
-	0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
-	0x22, 0x49, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
-	0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5a, 0x0a, 0x0e, 0x4c,
-	0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a,
+	0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+	0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
+	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72,
+	0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
+	0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d,
+	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x82, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x55,
+	0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
+	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73,
+	0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
+	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x58, 0x0a, 0x12,
+	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+	0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10,
+	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72,
+	0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x5f, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+	0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a,
 	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
 	0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73,
 	0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f,
 	0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
-	0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x58, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4e, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74,
 	0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
 	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
-	0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x24, 0x0a, 0x04, 0x75,
-	0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64,
-	0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65,
-	0x72, 0x22, 0x5f, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,
-	0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d,
-	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
-	0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x22, 0x45, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
-	0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
-	0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x82, 0x01, 0x0a, 0x10, 0x47, 0x65,
-	0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c,
+	0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x75,
+	0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x75,
+	0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5f, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74,
+	0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c,
 	0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
 	0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06,
 	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67,
 	0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72,
-	0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
-	0x62, 0x61, 0x63, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x58,
-	0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71,
+	0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5a, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61,
+	0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c,
+	0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x05,
+	0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f,
+	0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x05, 0x72,
+	0x6f, 0x6c, 0x65, 0x73, 0x22, 0x5f, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f,
+	0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74,
+	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64,
+	0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
+	0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61,
+	0x6d, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+	0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x12, 0x24, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
+	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x52, 0x6f, 0x6c, 0x65,
+	0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x2f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c,
+	0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d,
+	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x84, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52,
+	0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
+	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73,
+	0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
+	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18,
+	0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62,
+	0x61, 0x63, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x22, 0x5a,
+	0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71,
 	0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
 	0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
-	0x6d, 0x70, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e,
-	0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x73,
-	0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x5f, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a,
-	0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e,
-	0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4e, 0x0a, 0x12, 0x44, 0x65, 0x6c,
-	0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x6d, 0x70, 0x12, 0x26, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x52,
+	0x6f, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x22, 0x5f, 0x0a, 0x13, 0x55, 0x70,
+	0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
+	0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x1f,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x46, 0x6f, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
 	0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a,
-	0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
-	0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5f, 0x0a, 0x13, 0x44, 0x65, 0x6c,
-	0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a,
-	0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12,
-	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a, 0x41, 0x0a, 0x06, 0x53, 0x74,
-	0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55,
-	0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09,
-	0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53,
-	0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x2a, 0x39, 0x0a,
-	0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x10, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x55, 0x4e,
-	0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55,
-	0x53, 0x45, 0x52, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53,
-	0x45, 0x52, 0x5f, 0x50, 0x57, 0x44, 0x10, 0x02, 0x32, 0xcd, 0x04, 0x0a, 0x0b, 0x41, 0x75, 0x74,
-	0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69,
-	0x6e, 0x12, 0x18, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x4c,
-	0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x6f,
-	0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x3a, 0x01,
-	0x2a, 0x22, 0x06, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x5b, 0x0a, 0x06, 0x4c, 0x6f, 0x67,
-	0x6f, 0x75, 0x74, 0x12, 0x19, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63,
-	0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a,
-	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x6f,
-	0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93,
-	0x02, 0x14, 0x22, 0x12, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x2f, 0x7b, 0x75, 0x73, 0x65,
-	0x72, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x68, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62,
-	0x61, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62,
-	0x61, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01,
-	0x2a, 0x22, 0x0d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x12, 0x55, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x67,
-	0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
-	0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x73, 0x64,
-	0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x08, 0x12,
-	0x06, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x68, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74,
-	0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
-	0x62, 0x61, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52,
+	0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1b, 0x0a,
+	0x09, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65,
+	0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c,
+	0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x70, 0x65, 0x72, 0x6d, 0x69,
+	0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x6c,
+	0x0a, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69,
+	0x6f, 0x6e, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+	0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
+	0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4f, 0x0a, 0x12,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+	0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5f, 0x0a,
+	0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+	0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+	0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e,
+	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a, 0x41,
+	0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x41, 0x54,
+	0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
+	0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, 0x4b, 0x10, 0x01, 0x12,
+	0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
+	0x02, 0x32, 0xaa, 0x0a, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x12, 0x4f, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x18, 0x2e, 0x67, 0x6f, 0x73,
+	0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+	0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x3a, 0x01, 0x2a, 0x22, 0x06, 0x2f, 0x6c, 0x6f, 0x67,
+	0x69, 0x6e, 0x12, 0x5b, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x19, 0x2e, 0x67,
+	0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e,
+	0x72, 0x62, 0x61, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x12, 0x2f, 0x6c, 0x6f,
+	0x67, 0x6f, 0x75, 0x74, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12,
+	0x68, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1e,
+	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61,
+	0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f,
+	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61,
+	0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+	0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x75, 0x73, 0x65,
+	0x72, 0x73, 0x2f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x07, 0x47, 0x65, 0x74,
+	0x55, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65,
+	0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82,
+	0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x67, 0x65,
+	0x74, 0x12, 0x55, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e,
+	0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73,
+	0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x73,
+	0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x08,
+	0x12, 0x06, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x68, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61,
+	0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e,
+	0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e,
+	0x72, 0x62, 0x61, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12,
+	0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x75, 0x70, 0x64, 0x61,
+	0x74, 0x65, 0x12, 0x65, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
+	0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x44,
+	0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x44,
+	0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x2a, 0x0d, 0x2f, 0x75, 0x73, 0x65,
+	0x72, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x68, 0x0a, 0x0b, 0x43, 0x72, 0x65,
+	0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e,
+	0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65,
+	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e,
+	0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65,
+	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02,
+	0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x63, 0x72, 0x65,
+	0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x1a,
+	0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52,
+	0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x73,
+	0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12,
+	0x0a, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x65, 0x74, 0x12, 0x55, 0x0a, 0x08, 0x47,
+	0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e,
+	0x72, 0x62, 0x61, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x0e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x08, 0x12, 0x06, 0x2f, 0x72, 0x6f, 0x6c,
+	0x65, 0x73, 0x12, 0x68, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65,
+	0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f,
+	0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x98, 0x01, 0x0a,
+	0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x67, 0x6f, 0x73, 0x64,
+	0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72,
+	0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
+	0x62, 0x61, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
+	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x46, 0x6f, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x72,
+	0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x70, 0x65, 0x72, 0x6d,
+	0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x65, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74,
+	0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
+	0x62, 0x61, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52,
 	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
-	0x62, 0x61, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a,
-	0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74,
-	0x65, 0x12, 0x65, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73,
-	0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x44, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x1f, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x44, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x2a, 0x0d, 0x2f, 0x75, 0x73, 0x65, 0x72,
-	0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x63, 0x6f, 0x64, 0x65,
-	0x2e, 0x66, 0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e,
-	0x65, 0x74, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f,
-	0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x72, 0x62, 0x61, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x62, 0x61, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x2a,
+	0x0d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x30,
+	0x5a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66, 0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, 0x2e,
+	0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x61,
+	0x70, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x72, 0x62, 0x61, 0x63,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -958,51 +1999,94 @@ func file_gosdn_rbac_rbac_proto_rawDescGZIP() []byte {
 	return file_gosdn_rbac_rbac_proto_rawDescData
 }
 
-var file_gosdn_rbac_rbac_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
-var file_gosdn_rbac_rbac_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
+var file_gosdn_rbac_rbac_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_gosdn_rbac_rbac_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
 var file_gosdn_rbac_rbac_proto_goTypes = []interface{}{
-	(Status)(0),                 // 0: gosdn.rbac.Status
-	(User)(0),                   // 1: gosdn.rbac.User
-	(*LoginRequest)(nil),        // 2: gosdn.rbac.LoginRequest
-	(*LoginResponse)(nil),       // 3: gosdn.rbac.LoginResponse
-	(*LogoutRequest)(nil),       // 4: gosdn.rbac.LogoutRequest
-	(*LogoutResponse)(nil),      // 5: gosdn.rbac.LogoutResponse
-	(*CreateUsersRequest)(nil),  // 6: gosdn.rbac.CreateUsersRequest
-	(*CreateUsersResponse)(nil), // 7: gosdn.rbac.CreateUsersResponse
-	(*GetUsersRequest)(nil),     // 8: gosdn.rbac.GetUsersRequest
-	(*GetUsersResponse)(nil),    // 9: gosdn.rbac.GetUsersResponse
-	(*UpdateUsersRequest)(nil),  // 10: gosdn.rbac.UpdateUsersRequest
-	(*UpdateUsersResponse)(nil), // 11: gosdn.rbac.UpdateUsersResponse
-	(*DeleteUsersRequest)(nil),  // 12: gosdn.rbac.DeleteUsersRequest
-	(*DeleteUsersResponse)(nil), // 13: gosdn.rbac.DeleteUsersResponse
+	(Status)(0),                              // 0: gosdn.rbac.Status
+	(*User)(nil),                             // 1: gosdn.rbac.User
+	(*Role)(nil),                             // 2: gosdn.rbac.Role
+	(*LoginRequest)(nil),                     // 3: gosdn.rbac.LoginRequest
+	(*LoginResponse)(nil),                    // 4: gosdn.rbac.LoginResponse
+	(*LogoutRequest)(nil),                    // 5: gosdn.rbac.LogoutRequest
+	(*LogoutResponse)(nil),                   // 6: gosdn.rbac.LogoutResponse
+	(*CreateUsersRequest)(nil),               // 7: gosdn.rbac.CreateUsersRequest
+	(*CreateUsersResponse)(nil),              // 8: gosdn.rbac.CreateUsersResponse
+	(*GetUserRequest)(nil),                   // 9: gosdn.rbac.GetUserRequest
+	(*GetUserResponse)(nil),                  // 10: gosdn.rbac.GetUserResponse
+	(*GetUsersRequest)(nil),                  // 11: gosdn.rbac.GetUsersRequest
+	(*GetUsersResponse)(nil),                 // 12: gosdn.rbac.GetUsersResponse
+	(*UpdateUsersRequest)(nil),               // 13: gosdn.rbac.UpdateUsersRequest
+	(*UpdateUsersResponse)(nil),              // 14: gosdn.rbac.UpdateUsersResponse
+	(*DeleteUsersRequest)(nil),               // 15: gosdn.rbac.DeleteUsersRequest
+	(*DeleteUsersResponse)(nil),              // 16: gosdn.rbac.DeleteUsersResponse
+	(*CreateRolesRequest)(nil),               // 17: gosdn.rbac.CreateRolesRequest
+	(*CreateRolesResponse)(nil),              // 18: gosdn.rbac.CreateRolesResponse
+	(*GetRoleRequest)(nil),                   // 19: gosdn.rbac.GetRoleRequest
+	(*GetRoleResponse)(nil),                  // 20: gosdn.rbac.GetRoleResponse
+	(*GetRolesRequest)(nil),                  // 21: gosdn.rbac.GetRolesRequest
+	(*GetRolesResponse)(nil),                 // 22: gosdn.rbac.GetRolesResponse
+	(*UpdateRolesRequest)(nil),               // 23: gosdn.rbac.UpdateRolesRequest
+	(*UpdateRolesResponse)(nil),              // 24: gosdn.rbac.UpdateRolesResponse
+	(*DeletePermissionsForRoleRequest)(nil),  // 25: gosdn.rbac.DeletePermissionsForRoleRequest
+	(*DeletePermissionsForRoleResponse)(nil), // 26: gosdn.rbac.DeletePermissionsForRoleResponse
+	(*DeleteRolesRequest)(nil),               // 27: gosdn.rbac.DeleteRolesRequest
+	(*DeleteRolesResponse)(nil),              // 28: gosdn.rbac.DeleteRolesResponse
+	nil,                                      // 29: gosdn.rbac.User.RolesEntry
 }
 var file_gosdn_rbac_rbac_proto_depIdxs = []int32{
-	0,  // 0: gosdn.rbac.LoginResponse.status:type_name -> gosdn.rbac.Status
-	0,  // 1: gosdn.rbac.LogoutResponse.status:type_name -> gosdn.rbac.Status
-	1,  // 2: gosdn.rbac.CreateUsersRequest.user:type_name -> gosdn.rbac.User
-	0,  // 3: gosdn.rbac.CreateUsersResponse.status:type_name -> gosdn.rbac.Status
-	0,  // 4: gosdn.rbac.GetUsersResponse.status:type_name -> gosdn.rbac.Status
-	1,  // 5: gosdn.rbac.GetUsersResponse.user:type_name -> gosdn.rbac.User
-	1,  // 6: gosdn.rbac.UpdateUsersRequest.user:type_name -> gosdn.rbac.User
-	0,  // 7: gosdn.rbac.UpdateUsersResponse.status:type_name -> gosdn.rbac.Status
-	0,  // 8: gosdn.rbac.DeleteUsersResponse.status:type_name -> gosdn.rbac.Status
-	2,  // 9: gosdn.rbac.AuthService.Login:input_type -> gosdn.rbac.LoginRequest
-	4,  // 10: gosdn.rbac.AuthService.Logout:input_type -> gosdn.rbac.LogoutRequest
-	6,  // 11: gosdn.rbac.AuthService.CreateUsers:input_type -> gosdn.rbac.CreateUsersRequest
-	8,  // 12: gosdn.rbac.AuthService.GetUsers:input_type -> gosdn.rbac.GetUsersRequest
-	10, // 13: gosdn.rbac.AuthService.UpdateUsers:input_type -> gosdn.rbac.UpdateUsersRequest
-	12, // 14: gosdn.rbac.AuthService.DeleteUsers:input_type -> gosdn.rbac.DeleteUsersRequest
-	3,  // 15: gosdn.rbac.AuthService.Login:output_type -> gosdn.rbac.LoginResponse
-	5,  // 16: gosdn.rbac.AuthService.Logout:output_type -> gosdn.rbac.LogoutResponse
-	7,  // 17: gosdn.rbac.AuthService.CreateUsers:output_type -> gosdn.rbac.CreateUsersResponse
-	9,  // 18: gosdn.rbac.AuthService.GetUsers:output_type -> gosdn.rbac.GetUsersResponse
-	11, // 19: gosdn.rbac.AuthService.UpdateUsers:output_type -> gosdn.rbac.UpdateUsersResponse
-	13, // 20: gosdn.rbac.AuthService.DeleteUsers:output_type -> gosdn.rbac.DeleteUsersResponse
-	15, // [15:21] is the sub-list for method output_type
-	9,  // [9:15] is the sub-list for method input_type
-	9,  // [9:9] is the sub-list for extension type_name
-	9,  // [9:9] is the sub-list for extension extendee
-	0,  // [0:9] is the sub-list for field type_name
+	29, // 0: gosdn.rbac.User.roles:type_name -> gosdn.rbac.User.RolesEntry
+	0,  // 1: gosdn.rbac.LoginResponse.status:type_name -> gosdn.rbac.Status
+	0,  // 2: gosdn.rbac.LogoutResponse.status:type_name -> gosdn.rbac.Status
+	1,  // 3: gosdn.rbac.CreateUsersRequest.user:type_name -> gosdn.rbac.User
+	0,  // 4: gosdn.rbac.CreateUsersResponse.status:type_name -> gosdn.rbac.Status
+	0,  // 5: gosdn.rbac.GetUserResponse.status:type_name -> gosdn.rbac.Status
+	1,  // 6: gosdn.rbac.GetUserResponse.user:type_name -> gosdn.rbac.User
+	0,  // 7: gosdn.rbac.GetUsersResponse.status:type_name -> gosdn.rbac.Status
+	1,  // 8: gosdn.rbac.GetUsersResponse.user:type_name -> gosdn.rbac.User
+	1,  // 9: gosdn.rbac.UpdateUsersRequest.user:type_name -> gosdn.rbac.User
+	0,  // 10: gosdn.rbac.UpdateUsersResponse.status:type_name -> gosdn.rbac.Status
+	0,  // 11: gosdn.rbac.DeleteUsersResponse.status:type_name -> gosdn.rbac.Status
+	2,  // 12: gosdn.rbac.CreateRolesRequest.roles:type_name -> gosdn.rbac.Role
+	0,  // 13: gosdn.rbac.CreateRolesResponse.status:type_name -> gosdn.rbac.Status
+	0,  // 14: gosdn.rbac.GetRoleResponse.status:type_name -> gosdn.rbac.Status
+	2,  // 15: gosdn.rbac.GetRoleResponse.role:type_name -> gosdn.rbac.Role
+	0,  // 16: gosdn.rbac.GetRolesResponse.status:type_name -> gosdn.rbac.Status
+	2,  // 17: gosdn.rbac.GetRolesResponse.roles:type_name -> gosdn.rbac.Role
+	2,  // 18: gosdn.rbac.UpdateRolesRequest.roles:type_name -> gosdn.rbac.Role
+	0,  // 19: gosdn.rbac.UpdateRolesResponse.status:type_name -> gosdn.rbac.Status
+	0,  // 20: gosdn.rbac.DeletePermissionsForRoleResponse.status:type_name -> gosdn.rbac.Status
+	0,  // 21: gosdn.rbac.DeleteRolesResponse.status:type_name -> gosdn.rbac.Status
+	3,  // 22: gosdn.rbac.AuthService.Login:input_type -> gosdn.rbac.LoginRequest
+	5,  // 23: gosdn.rbac.AuthService.Logout:input_type -> gosdn.rbac.LogoutRequest
+	7,  // 24: gosdn.rbac.AuthService.CreateUsers:input_type -> gosdn.rbac.CreateUsersRequest
+	9,  // 25: gosdn.rbac.AuthService.GetUser:input_type -> gosdn.rbac.GetUserRequest
+	11, // 26: gosdn.rbac.AuthService.GetUsers:input_type -> gosdn.rbac.GetUsersRequest
+	13, // 27: gosdn.rbac.AuthService.UpdateUsers:input_type -> gosdn.rbac.UpdateUsersRequest
+	15, // 28: gosdn.rbac.AuthService.DeleteUsers:input_type -> gosdn.rbac.DeleteUsersRequest
+	17, // 29: gosdn.rbac.AuthService.CreateRoles:input_type -> gosdn.rbac.CreateRolesRequest
+	19, // 30: gosdn.rbac.AuthService.GetRole:input_type -> gosdn.rbac.GetRoleRequest
+	21, // 31: gosdn.rbac.AuthService.GetRoles:input_type -> gosdn.rbac.GetRolesRequest
+	23, // 32: gosdn.rbac.AuthService.UpdateRoles:input_type -> gosdn.rbac.UpdateRolesRequest
+	25, // 33: gosdn.rbac.AuthService.DeletePermissionsForRole:input_type -> gosdn.rbac.DeletePermissionsForRoleRequest
+	27, // 34: gosdn.rbac.AuthService.DeleteRoles:input_type -> gosdn.rbac.DeleteRolesRequest
+	4,  // 35: gosdn.rbac.AuthService.Login:output_type -> gosdn.rbac.LoginResponse
+	6,  // 36: gosdn.rbac.AuthService.Logout:output_type -> gosdn.rbac.LogoutResponse
+	8,  // 37: gosdn.rbac.AuthService.CreateUsers:output_type -> gosdn.rbac.CreateUsersResponse
+	10, // 38: gosdn.rbac.AuthService.GetUser:output_type -> gosdn.rbac.GetUserResponse
+	12, // 39: gosdn.rbac.AuthService.GetUsers:output_type -> gosdn.rbac.GetUsersResponse
+	14, // 40: gosdn.rbac.AuthService.UpdateUsers:output_type -> gosdn.rbac.UpdateUsersResponse
+	16, // 41: gosdn.rbac.AuthService.DeleteUsers:output_type -> gosdn.rbac.DeleteUsersResponse
+	18, // 42: gosdn.rbac.AuthService.CreateRoles:output_type -> gosdn.rbac.CreateRolesResponse
+	20, // 43: gosdn.rbac.AuthService.GetRole:output_type -> gosdn.rbac.GetRoleResponse
+	22, // 44: gosdn.rbac.AuthService.GetRoles:output_type -> gosdn.rbac.GetRolesResponse
+	24, // 45: gosdn.rbac.AuthService.UpdateRoles:output_type -> gosdn.rbac.UpdateRolesResponse
+	26, // 46: gosdn.rbac.AuthService.DeletePermissionsForRole:output_type -> gosdn.rbac.DeletePermissionsForRoleResponse
+	28, // 47: gosdn.rbac.AuthService.DeleteRoles:output_type -> gosdn.rbac.DeleteRolesResponse
+	35, // [35:48] is the sub-list for method output_type
+	22, // [22:35] is the sub-list for method input_type
+	22, // [22:22] is the sub-list for extension type_name
+	22, // [22:22] is the sub-list for extension extendee
+	0,  // [0:22] is the sub-list for field type_name
 }
 
 func init() { file_gosdn_rbac_rbac_proto_init() }
@@ -1012,7 +2096,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 	}
 	if !protoimpl.UnsafeEnabled {
 		file_gosdn_rbac_rbac_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*LoginRequest); i {
+			switch v := v.(*User); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1024,7 +2108,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*LoginResponse); i {
+			switch v := v.(*Role); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1036,7 +2120,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*LogoutRequest); i {
+			switch v := v.(*LoginRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1048,7 +2132,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*LogoutResponse); i {
+			switch v := v.(*LoginResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1060,7 +2144,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CreateUsersRequest); i {
+			switch v := v.(*LogoutRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1072,7 +2156,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CreateUsersResponse); i {
+			switch v := v.(*LogoutResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1084,7 +2168,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetUsersRequest); i {
+			switch v := v.(*CreateUsersRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1096,7 +2180,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetUsersResponse); i {
+			switch v := v.(*CreateUsersResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1108,7 +2192,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UpdateUsersRequest); i {
+			switch v := v.(*GetUserRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1120,7 +2204,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UpdateUsersResponse); i {
+			switch v := v.(*GetUserResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1132,7 +2216,7 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DeleteUsersRequest); i {
+			switch v := v.(*GetUsersRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1144,6 +2228,54 @@ func file_gosdn_rbac_rbac_proto_init() {
 			}
 		}
 		file_gosdn_rbac_rbac_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetUsersResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UpdateUsersRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UpdateUsersResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeleteUsersRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DeleteUsersResponse); i {
 			case 0:
 				return &v.state
@@ -1155,14 +2287,158 @@ func file_gosdn_rbac_rbac_proto_init() {
 				return nil
 			}
 		}
+		file_gosdn_rbac_rbac_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CreateRolesRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CreateRolesResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetRoleRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetRoleResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetRolesRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetRolesResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UpdateRolesRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UpdateRolesResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeletePermissionsForRoleRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeletePermissionsForRoleResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeleteRolesRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_gosdn_rbac_rbac_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeleteRolesResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_gosdn_rbac_rbac_proto_rawDesc,
-			NumEnums:      2,
-			NumMessages:   12,
+			NumEnums:      1,
+			NumMessages:   29,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/api/go/gosdn/rbac/rbac.pb.gw.go b/api/go/gosdn/rbac/rbac.pb.gw.go
index 23cff1f998c166d7471cc0c9b040a2139587e290..d5a6a249c7f645750c501e9826e9df51aaa37ece 100644
--- a/api/go/gosdn/rbac/rbac.pb.gw.go
+++ b/api/go/gosdn/rbac/rbac.pb.gw.go
@@ -169,6 +169,42 @@ func local_request_AuthService_CreateUsers_0(ctx context.Context, marshaler runt
 
 }
 
+var (
+	filter_AuthService_GetUser_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_GetUser_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetUserRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_GetUser_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GetUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_GetUser_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetUserRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_GetUser_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GetUser(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 var (
 	filter_AuthService_GetUsers_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
 )
@@ -275,6 +311,218 @@ func local_request_AuthService_DeleteUsers_0(ctx context.Context, marshaler runt
 
 }
 
+func request_AuthService_CreateRoles_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq CreateRolesRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.CreateRoles(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_CreateRoles_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq CreateRolesRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.CreateRoles(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_GetRole_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_GetRole_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetRoleRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_GetRole_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GetRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_GetRole_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetRoleRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_GetRole_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GetRole(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_GetRoles_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_GetRoles_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetRolesRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_GetRoles_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GetRoles(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_GetRoles_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetRolesRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_GetRoles_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GetRoles(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_AuthService_UpdateRoles_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq UpdateRolesRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.UpdateRoles(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_UpdateRoles_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq UpdateRolesRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.UpdateRoles(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_DeletePermissionsForRole_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_DeletePermissionsForRole_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq DeletePermissionsForRoleRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_DeletePermissionsForRole_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.DeletePermissionsForRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_DeletePermissionsForRole_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq DeletePermissionsForRoleRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_DeletePermissionsForRole_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.DeletePermissionsForRole(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_DeleteRoles_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_DeleteRoles_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq DeleteRolesRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_DeleteRoles_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.DeleteRoles(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_DeleteRoles_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq DeleteRolesRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_DeleteRoles_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.DeleteRoles(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 // RegisterAuthServiceHandlerServer registers the http handlers for service AuthService to "mux".
 // UnaryRPC     :call AuthServiceServer directly.
 // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -350,6 +598,29 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
 
 	})
 
+	mux.Handle("GET", pattern_AuthService_GetUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetUser", runtime.WithHTTPPathPattern("/users/get"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_GetUser_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_GetUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("GET", pattern_AuthService_GetUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -419,6 +690,144 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
 
 	})
 
+	mux.Handle("POST", pattern_AuthService_CreateRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/CreateRoles", runtime.WithHTTPPathPattern("/roles/create"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_CreateRoles_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_CreateRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_AuthService_GetRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetRole", runtime.WithHTTPPathPattern("/roles/get"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_GetRole_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_GetRole_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_AuthService_GetRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetRoles", runtime.WithHTTPPathPattern("/roles"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_GetRoles_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_GetRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_UpdateRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/UpdateRoles", runtime.WithHTTPPathPattern("/roles/update"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_UpdateRoles_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_UpdateRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("DELETE", pattern_AuthService_DeletePermissionsForRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/DeletePermissionsForRole", runtime.WithHTTPPathPattern("/roles/delete/permissions"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_DeletePermissionsForRole_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_DeletePermissionsForRole_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("DELETE", pattern_AuthService_DeleteRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gosdn.rbac.AuthService/DeleteRoles", runtime.WithHTTPPathPattern("/roles/delete"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_DeleteRoles_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_DeleteRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -520,6 +929,26 @@ func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
 
 	})
 
+	mux.Handle("GET", pattern_AuthService_GetUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetUser", runtime.WithHTTPPathPattern("/users/get"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_GetUser_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_GetUser_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("GET", pattern_AuthService_GetUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -580,6 +1009,126 @@ func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
 
 	})
 
+	mux.Handle("POST", pattern_AuthService_CreateRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/CreateRoles", runtime.WithHTTPPathPattern("/roles/create"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_CreateRoles_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_CreateRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_AuthService_GetRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetRole", runtime.WithHTTPPathPattern("/roles/get"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_GetRole_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_GetRole_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_AuthService_GetRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetRoles", runtime.WithHTTPPathPattern("/roles"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_GetRoles_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_GetRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_UpdateRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/UpdateRoles", runtime.WithHTTPPathPattern("/roles/update"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_UpdateRoles_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_UpdateRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("DELETE", pattern_AuthService_DeletePermissionsForRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/DeletePermissionsForRole", runtime.WithHTTPPathPattern("/roles/delete/permissions"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_DeletePermissionsForRole_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_DeletePermissionsForRole_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("DELETE", pattern_AuthService_DeleteRoles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/DeleteRoles", runtime.WithHTTPPathPattern("/roles/delete"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_DeleteRoles_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_AuthService_DeleteRoles_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 }
 
@@ -590,11 +1139,25 @@ var (
 
 	pattern_AuthService_CreateUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"users", "create"}, ""))
 
+	pattern_AuthService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"users", "get"}, ""))
+
 	pattern_AuthService_GetUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"users"}, ""))
 
 	pattern_AuthService_UpdateUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"users", "update"}, ""))
 
 	pattern_AuthService_DeleteUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"users", "delete"}, ""))
+
+	pattern_AuthService_CreateRoles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"roles", "create"}, ""))
+
+	pattern_AuthService_GetRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"roles", "get"}, ""))
+
+	pattern_AuthService_GetRoles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"roles"}, ""))
+
+	pattern_AuthService_UpdateRoles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"roles", "update"}, ""))
+
+	pattern_AuthService_DeletePermissionsForRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"roles", "delete", "permissions"}, ""))
+
+	pattern_AuthService_DeleteRoles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"roles", "delete"}, ""))
 )
 
 var (
@@ -604,9 +1167,23 @@ var (
 
 	forward_AuthService_CreateUsers_0 = runtime.ForwardResponseMessage
 
+	forward_AuthService_GetUser_0 = runtime.ForwardResponseMessage
+
 	forward_AuthService_GetUsers_0 = runtime.ForwardResponseMessage
 
 	forward_AuthService_UpdateUsers_0 = runtime.ForwardResponseMessage
 
 	forward_AuthService_DeleteUsers_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_CreateRoles_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_GetRole_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_GetRoles_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_UpdateRoles_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_DeletePermissionsForRole_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_DeleteRoles_0 = runtime.ForwardResponseMessage
 )
diff --git a/api/go/gosdn/rbac/rbac_grpc.pb.go b/api/go/gosdn/rbac/rbac_grpc.pb.go
index 781a8fd9b64fe92b2c468604a83daec3376e966f..8ba7a4f688c04eab25cfa6115824fa7519b102ad 100644
--- a/api/go/gosdn/rbac/rbac_grpc.pb.go
+++ b/api/go/gosdn/rbac/rbac_grpc.pb.go
@@ -26,16 +26,36 @@ type AuthServiceClient interface {
 	// Highest possible permissions of new users is of current permission level.
 	// If not logged in: Created user has lowest possible permissions, only one user can be created this way.
 	CreateUsers(ctx context.Context, in *CreateUsersRequest, opts ...grpc.CallOption) (*CreateUsersResponse, error)
+	// Requests information about one user, requires login beforehand.option
+	GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error)
 	// Requests information about available users, requires login beforehand.
 	// Requires highest possible permissions.
 	GetUsers(ctx context.Context, in *GetUsersRequest, opts ...grpc.CallOption) (*GetUsersResponse, error)
 	// Updates users with the provided parameters, requires login beforehand.
 	// Requires highest permissions to change other users, everyone else can only update their own account.
-	// // Requires highest permissions to change multiple users at the same time.
+	// Requires highest permissions to change multiple users at the same time.
 	UpdateUsers(ctx context.Context, in *UpdateUsersRequest, opts ...grpc.CallOption) (*UpdateUsersResponse, error)
 	// Deletes users, requires login beforehand.
 	// Requires highest permissions.
 	DeleteUsers(ctx context.Context, in *DeleteUsersRequest, opts ...grpc.CallOption) (*DeleteUsersResponse, error)
+	// Creates roles, requires login beforehand.
+	// Requires highest permissions.
+	CreateRoles(ctx context.Context, in *CreateRolesRequest, opts ...grpc.CallOption) (*CreateRolesResponse, error)
+	// Requests one role with its permissions, requires login beforehand.
+	// Requires highest permissions.
+	GetRole(ctx context.Context, in *GetRoleRequest, opts ...grpc.CallOption) (*GetRoleResponse, error)
+	// Requests all roles with their permissions, requires login beforehand.
+	// Requires highest permissions.
+	GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesResponse, error)
+	// Updates roles by setting the provided permissions, requires login beforehand.
+	// Requires highest permissions.
+	UpdateRoles(ctx context.Context, in *UpdateRolesRequest, opts ...grpc.CallOption) (*UpdateRolesResponse, error)
+	// Deletes permissions from given role, requires login beforehand.
+	// Requires highest permissions.
+	DeletePermissionsForRole(ctx context.Context, in *DeletePermissionsForRoleRequest, opts ...grpc.CallOption) (*DeletePermissionsForRoleResponse, error)
+	// Deletes roles with their permissions, requires login beforehand.
+	// Requires highest permissions.
+	DeleteRoles(ctx context.Context, in *DeleteRolesRequest, opts ...grpc.CallOption) (*DeleteRolesResponse, error)
 }
 
 type authServiceClient struct {
@@ -73,6 +93,15 @@ func (c *authServiceClient) CreateUsers(ctx context.Context, in *CreateUsersRequ
 	return out, nil
 }
 
+func (c *authServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) {
+	out := new(GetUserResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/GetUser", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *authServiceClient) GetUsers(ctx context.Context, in *GetUsersRequest, opts ...grpc.CallOption) (*GetUsersResponse, error) {
 	out := new(GetUsersResponse)
 	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/GetUsers", in, out, opts...)
@@ -100,6 +129,60 @@ func (c *authServiceClient) DeleteUsers(ctx context.Context, in *DeleteUsersRequ
 	return out, nil
 }
 
+func (c *authServiceClient) CreateRoles(ctx context.Context, in *CreateRolesRequest, opts ...grpc.CallOption) (*CreateRolesResponse, error) {
+	out := new(CreateRolesResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/CreateRoles", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) GetRole(ctx context.Context, in *GetRoleRequest, opts ...grpc.CallOption) (*GetRoleResponse, error) {
+	out := new(GetRoleResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/GetRole", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesResponse, error) {
+	out := new(GetRolesResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/GetRoles", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) UpdateRoles(ctx context.Context, in *UpdateRolesRequest, opts ...grpc.CallOption) (*UpdateRolesResponse, error) {
+	out := new(UpdateRolesResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/UpdateRoles", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) DeletePermissionsForRole(ctx context.Context, in *DeletePermissionsForRoleRequest, opts ...grpc.CallOption) (*DeletePermissionsForRoleResponse, error) {
+	out := new(DeletePermissionsForRoleResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/DeletePermissionsForRole", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) DeleteRoles(ctx context.Context, in *DeleteRolesRequest, opts ...grpc.CallOption) (*DeleteRolesResponse, error) {
+	out := new(DeleteRolesResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/DeleteRoles", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // AuthServiceServer is the server API for AuthService service.
 // All implementations must embed UnimplementedAuthServiceServer
 // for forward compatibility
@@ -112,16 +195,36 @@ type AuthServiceServer interface {
 	// Highest possible permissions of new users is of current permission level.
 	// If not logged in: Created user has lowest possible permissions, only one user can be created this way.
 	CreateUsers(context.Context, *CreateUsersRequest) (*CreateUsersResponse, error)
+	// Requests information about one user, requires login beforehand.option
+	GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error)
 	// Requests information about available users, requires login beforehand.
 	// Requires highest possible permissions.
 	GetUsers(context.Context, *GetUsersRequest) (*GetUsersResponse, error)
 	// Updates users with the provided parameters, requires login beforehand.
 	// Requires highest permissions to change other users, everyone else can only update their own account.
-	// // Requires highest permissions to change multiple users at the same time.
+	// Requires highest permissions to change multiple users at the same time.
 	UpdateUsers(context.Context, *UpdateUsersRequest) (*UpdateUsersResponse, error)
 	// Deletes users, requires login beforehand.
 	// Requires highest permissions.
 	DeleteUsers(context.Context, *DeleteUsersRequest) (*DeleteUsersResponse, error)
+	// Creates roles, requires login beforehand.
+	// Requires highest permissions.
+	CreateRoles(context.Context, *CreateRolesRequest) (*CreateRolesResponse, error)
+	// Requests one role with its permissions, requires login beforehand.
+	// Requires highest permissions.
+	GetRole(context.Context, *GetRoleRequest) (*GetRoleResponse, error)
+	// Requests all roles with their permissions, requires login beforehand.
+	// Requires highest permissions.
+	GetRoles(context.Context, *GetRolesRequest) (*GetRolesResponse, error)
+	// Updates roles by setting the provided permissions, requires login beforehand.
+	// Requires highest permissions.
+	UpdateRoles(context.Context, *UpdateRolesRequest) (*UpdateRolesResponse, error)
+	// Deletes permissions from given role, requires login beforehand.
+	// Requires highest permissions.
+	DeletePermissionsForRole(context.Context, *DeletePermissionsForRoleRequest) (*DeletePermissionsForRoleResponse, error)
+	// Deletes roles with their permissions, requires login beforehand.
+	// Requires highest permissions.
+	DeleteRoles(context.Context, *DeleteRolesRequest) (*DeleteRolesResponse, error)
 	mustEmbedUnimplementedAuthServiceServer()
 }
 
@@ -138,6 +241,9 @@ func (UnimplementedAuthServiceServer) Logout(context.Context, *LogoutRequest) (*
 func (UnimplementedAuthServiceServer) CreateUsers(context.Context, *CreateUsersRequest) (*CreateUsersResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method CreateUsers not implemented")
 }
+func (UnimplementedAuthServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
+}
 func (UnimplementedAuthServiceServer) GetUsers(context.Context, *GetUsersRequest) (*GetUsersResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GetUsers not implemented")
 }
@@ -147,6 +253,24 @@ func (UnimplementedAuthServiceServer) UpdateUsers(context.Context, *UpdateUsersR
 func (UnimplementedAuthServiceServer) DeleteUsers(context.Context, *DeleteUsersRequest) (*DeleteUsersResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method DeleteUsers not implemented")
 }
+func (UnimplementedAuthServiceServer) CreateRoles(context.Context, *CreateRolesRequest) (*CreateRolesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CreateRoles not implemented")
+}
+func (UnimplementedAuthServiceServer) GetRole(context.Context, *GetRoleRequest) (*GetRoleResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetRole not implemented")
+}
+func (UnimplementedAuthServiceServer) GetRoles(context.Context, *GetRolesRequest) (*GetRolesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetRoles not implemented")
+}
+func (UnimplementedAuthServiceServer) UpdateRoles(context.Context, *UpdateRolesRequest) (*UpdateRolesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method UpdateRoles not implemented")
+}
+func (UnimplementedAuthServiceServer) DeletePermissionsForRole(context.Context, *DeletePermissionsForRoleRequest) (*DeletePermissionsForRoleResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DeletePermissionsForRole not implemented")
+}
+func (UnimplementedAuthServiceServer) DeleteRoles(context.Context, *DeleteRolesRequest) (*DeleteRolesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DeleteRoles not implemented")
+}
 func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
 
 // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service.
@@ -214,6 +338,24 @@ func _AuthService_CreateUsers_Handler(srv interface{}, ctx context.Context, dec
 	return interceptor(ctx, in, info, handler)
 }
 
+func _AuthService_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetUserRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).GetUser(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/GetUser",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).GetUser(ctx, req.(*GetUserRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _AuthService_GetUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(GetUsersRequest)
 	if err := dec(in); err != nil {
@@ -268,6 +410,114 @@ func _AuthService_DeleteUsers_Handler(srv interface{}, ctx context.Context, dec
 	return interceptor(ctx, in, info, handler)
 }
 
+func _AuthService_CreateRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(CreateRolesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).CreateRoles(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/CreateRoles",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).CreateRoles(ctx, req.(*CreateRolesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_GetRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetRoleRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).GetRole(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/GetRole",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).GetRole(ctx, req.(*GetRoleRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_GetRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetRolesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).GetRoles(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/GetRoles",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).GetRoles(ctx, req.(*GetRolesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_UpdateRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(UpdateRolesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).UpdateRoles(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/UpdateRoles",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).UpdateRoles(ctx, req.(*UpdateRolesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_DeletePermissionsForRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(DeletePermissionsForRoleRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).DeletePermissionsForRole(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/DeletePermissionsForRole",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).DeletePermissionsForRole(ctx, req.(*DeletePermissionsForRoleRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_DeleteRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(DeleteRolesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).DeleteRoles(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/DeleteRoles",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).DeleteRoles(ctx, req.(*DeleteRolesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 // AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service.
 // It's only intended for direct use with grpc.RegisterService,
 // and not to be introspected or modified (even as a copy)
@@ -287,6 +537,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "CreateUsers",
 			Handler:    _AuthService_CreateUsers_Handler,
 		},
+		{
+			MethodName: "GetUser",
+			Handler:    _AuthService_GetUser_Handler,
+		},
 		{
 			MethodName: "GetUsers",
 			Handler:    _AuthService_GetUsers_Handler,
@@ -299,6 +553,30 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "DeleteUsers",
 			Handler:    _AuthService_DeleteUsers_Handler,
 		},
+		{
+			MethodName: "CreateRoles",
+			Handler:    _AuthService_CreateRoles_Handler,
+		},
+		{
+			MethodName: "GetRole",
+			Handler:    _AuthService_GetRole_Handler,
+		},
+		{
+			MethodName: "GetRoles",
+			Handler:    _AuthService_GetRoles_Handler,
+		},
+		{
+			MethodName: "UpdateRoles",
+			Handler:    _AuthService_UpdateRoles_Handler,
+		},
+		{
+			MethodName: "DeletePermissionsForRole",
+			Handler:    _AuthService_DeletePermissionsForRole_Handler,
+		},
+		{
+			MethodName: "DeleteRoles",
+			Handler:    _AuthService_DeleteRoles_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "gosdn/rbac/rbac.proto",
diff --git a/api/openapiv2/gosdn_northbound.swagger.json b/api/openapiv2/gosdn_northbound.swagger.json
index d7787740e84767031e74059eb15666340add3b6c..95b7be71018bc35e08df3ed6b008d4a14773b659 100644
--- a/api/openapiv2/gosdn_northbound.swagger.json
+++ b/api/openapiv2/gosdn_northbound.swagger.json
@@ -809,15 +809,80 @@
         ]
       }
     },
-    "/users": {
+    "/roles": {
       "get": {
-        "summary": "Requests information about available users, requires login beforehand.\nRequires highest possible permissions.",
-        "operationId": "AuthService_GetUsers",
+        "summary": "Requests all roles with their permissions, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_GetRoles",
         "responses": {
           "200": {
             "description": "A successful response.",
             "schema": {
-              "$ref": "#/definitions/rbacGetUsersResponse"
+              "$ref": "#/definitions/rbacGetRolesResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/roles/create": {
+      "post": {
+        "summary": "Creates roles, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_CreateRoles",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacCreateRolesResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/rbacCreateRolesRequest"
+            }
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/roles/delete": {
+      "delete": {
+        "summary": "Deletes roles with their permissions, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_DeleteRoles",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacDeleteRolesResponse"
             }
           },
           "default": {
@@ -836,7 +901,97 @@
             "format": "int64"
           },
           {
-            "name": "token",
+            "name": "roleName",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "multi"
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/roles/delete/permissions": {
+      "delete": {
+        "summary": "Deletes permissions from given role, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_DeletePermissionsForRole",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacDeletePermissionsForRoleResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          },
+          {
+            "name": "roleName",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "permissionsToDelete",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "multi"
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/roles/get": {
+      "get": {
+        "summary": "Requests one role with its permissions, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_GetRole",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacGetRoleResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          },
+          {
+            "name": "roleName",
             "in": "query",
             "required": false,
             "type": "string"
@@ -847,6 +1002,71 @@
         ]
       }
     },
+    "/roles/update": {
+      "post": {
+        "summary": "Updates roles by setting the provided permissions, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_UpdateRoles",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacUpdateRolesResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/rbacUpdateRolesRequest"
+            }
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/users": {
+      "get": {
+        "summary": "Requests information about available users, requires login beforehand.\nRequires highest possible permissions.",
+        "operationId": "AuthService_GetUsers",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacGetUsersResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
     "/users/create": {
       "post": {
         "summary": "Create users with the provided parameters, creation of multiple users requires login beforehand.\nHighest possible permissions of new users is of current permission level.\nIf not logged in: Created user has lowest possible permissions, only one user can be created this way.",
@@ -922,9 +1142,47 @@
         ]
       }
     },
+    "/users/get": {
+      "get": {
+        "summary": "Requests information about one user, requires login beforehand.option",
+        "operationId": "AuthService_GetUser",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacGetUserResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          },
+          {
+            "name": "name",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
     "/users/update": {
       "post": {
-        "summary": "Updates users with the provided parameters, requires login beforehand.\nRequires highest permissions to change other users, everyone else can only update their own account.\n// Requires highest permissions to change multiple users at the same time.",
+        "summary": "Updates users with the provided parameters, requires login beforehand.\nRequires highest permissions to change other users, everyone else can only update their own account.\nRequires highest permissions to change multiple users at the same time.",
         "operationId": "AuthService_UpdateUsers",
         "responses": {
           "200": {
@@ -2229,6 +2487,26 @@
       ],
       "default": "STATUS_UNSPECIFIED"
     },
+    "gosdnrbacRole": {
+      "type": "object",
+      "properties": {
+        "id": {
+          "type": "string"
+        },
+        "name": {
+          "type": "string"
+        },
+        "description": {
+          "type": "string"
+        },
+        "permissions": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
     "gosdnrbacStatus": {
       "type": "string",
       "enum": [
@@ -2620,6 +2898,34 @@
       "additionalProperties": {},
       "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n    Foo foo = ...;\n    Any any;\n    any.PackFrom(foo);\n    ...\n    if (any.UnpackTo(\u0026foo)) {\n      ...\n    }\n\nExample 2: Pack and unpack a message in Java.\n\n    Foo foo = ...;\n    Any any = Any.pack(foo);\n    ...\n    if (any.is(Foo.class)) {\n      foo = any.unpack(Foo.class);\n    }\n\n Example 3: Pack and unpack a message in Python.\n\n    foo = Foo(...)\n    any = Any()\n    any.Pack(foo)\n    ...\n    if any.Is(Foo.DESCRIPTOR):\n      any.Unpack(foo)\n      ...\n\n Example 4: Pack and unpack a message in Go\n\n     foo := \u0026pb.Foo{...}\n     any, err := anypb.New(foo)\n     if err != nil {\n       ...\n     }\n     ...\n     foo := \u0026pb.Foo{}\n     if err := any.UnmarshalTo(foo); err != nil {\n       ...\n     }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n    package google.profile;\n    message Person {\n      string first_name = 1;\n      string last_name = 2;\n    }\n\n    {\n      \"@type\": \"type.googleapis.com/google.profile.Person\",\n      \"firstName\": \u003cstring\u003e,\n      \"lastName\": \u003cstring\u003e\n    }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n    {\n      \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n      \"value\": \"1.212s\"\n    }"
     },
+    "rbacCreateRolesRequest": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "roles": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/gosdnrbacRole"
+          }
+        }
+      },
+      "title": "CreateRoles"
+    },
+    "rbacCreateRolesResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
     "rbacCreateUsersRequest": {
       "type": "object",
       "properties": {
@@ -2648,6 +2954,30 @@
         }
       }
     },
+    "rbacDeletePermissionsForRoleResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
+    "rbacDeleteRolesResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
     "rbacDeleteUsersResponse": {
       "type": "object",
       "properties": {
@@ -2660,6 +2990,54 @@
         }
       }
     },
+    "rbacGetRoleResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        },
+        "role": {
+          "$ref": "#/definitions/gosdnrbacRole"
+        }
+      }
+    },
+    "rbacGetRolesResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        },
+        "roles": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/gosdnrbacRole"
+          }
+        }
+      }
+    },
+    "rbacGetUserResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        },
+        "user": {
+          "$ref": "#/definitions/rbacUser"
+        }
+      }
+    },
     "rbacGetUsersResponse": {
       "type": "object",
       "properties": {
@@ -2721,6 +3099,34 @@
         }
       }
     },
+    "rbacUpdateRolesRequest": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "roles": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/gosdnrbacRole"
+          }
+        }
+      },
+      "title": "UpdateRoles"
+    },
+    "rbacUpdateRolesResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
     "rbacUpdateUsersRequest": {
       "type": "object",
       "properties": {
@@ -2750,14 +3156,27 @@
       }
     },
     "rbacUser": {
-      "type": "string",
-      "enum": [
-        "USER_UNSPECIFIED",
-        "USER_NAME",
-        "USER_PWD"
-      ],
-      "default": "USER_UNSPECIFIED",
-      "title": "TODO: add additional data to user enum"
+      "type": "object",
+      "properties": {
+        "id": {
+          "type": "string"
+        },
+        "name": {
+          "type": "string"
+        },
+        "roles": {
+          "type": "object",
+          "additionalProperties": {
+            "type": "string"
+          }
+        },
+        "password": {
+          "type": "string"
+        },
+        "token": {
+          "type": "string"
+        }
+      }
     },
     "southboundSouthboundInterface": {
       "type": "object",
diff --git a/api/proto/buf.lock b/api/proto/buf.lock
index 18424600585920365ca34c2abca5d6ade0947705..cbd6cb43b338d5f4f6833b84cfc1f5ae1ee0510b 100644
--- a/api/proto/buf.lock
+++ b/api/proto/buf.lock
@@ -4,7 +4,7 @@ deps:
   - remote: buf.build
     owner: googleapis
     repository: googleapis
-    commit: 0f45818f23164927b753ca450a5ed5bf
+    commit: b6631422c6964600bf3bdd9bf09c6636
   - remote: buf.build
     owner: grpc-ecosystem
     repository: grpc-gateway
diff --git a/api/proto/gosdn/rbac/rbac.proto b/api/proto/gosdn/rbac/rbac.proto
index 9145b8d64c8950d723b73c897b72e8f500fa6587..6208518c6ec663aecd4855270924ff4944bf6428 100644
--- a/api/proto/gosdn/rbac/rbac.proto
+++ b/api/proto/gosdn/rbac/rbac.proto
@@ -35,6 +35,13 @@ service AuthService {
         };
     }
 
+    // Requests information about one user, requires login beforehand.option
+    rpc GetUser (GetUserRequest) returns (GetUserResponse) {
+        option (google.api.http) = {
+            get: "/users/get"
+        };
+    }
+
     // Requests information about available users, requires login beforehand.
     // Requires highest possible permissions.
     rpc GetUsers (GetUsersRequest) returns (GetUsersResponse) {
@@ -45,7 +52,7 @@ service AuthService {
 
     // Updates users with the provided parameters, requires login beforehand.
     // Requires highest permissions to change other users, everyone else can only update their own account.
-    // // Requires highest permissions to change multiple users at the same time.
+    // Requires highest permissions to change multiple users at the same time.
     rpc UpdateUsers (UpdateUsersRequest) returns (UpdateUsersResponse) {
         option (google.api.http) = {
             post: "/users/update"
@@ -61,6 +68,55 @@ service AuthService {
         };
     }
 
+    // Creates roles, requires login beforehand.
+    // Requires highest permissions.
+    rpc CreateRoles(CreateRolesRequest) returns (CreateRolesResponse) {
+        option (google.api.http) = {
+            post: "/roles/create"
+            body: "*"
+        };
+    }
+
+    // Requests one role with its permissions, requires login beforehand.
+    // Requires highest permissions.
+    rpc GetRole(GetRoleRequest) returns (GetRoleResponse) {
+        option (google.api.http) = {
+            get: "/roles/get"
+        };
+    }
+
+    // Requests all roles with their permissions, requires login beforehand.
+    // Requires highest permissions.
+    rpc GetRoles(GetRolesRequest) returns (GetRolesResponse) {
+        option (google.api.http) = {
+            get: "/roles"
+        };
+    }
+
+    // Updates roles by setting the provided permissions, requires login beforehand.
+    // Requires highest permissions.
+    rpc UpdateRoles(UpdateRolesRequest) returns (UpdateRolesResponse) {
+        option (google.api.http) = {
+            post: "/roles/update"
+            body: "*"
+        };
+    }
+
+    // Deletes permissions from given role, requires login beforehand.
+    // Requires highest permissions.
+    rpc DeletePermissionsForRole(DeletePermissionsForRoleRequest) returns (DeletePermissionsForRoleResponse) {
+        option (google.api.http) = {
+            delete: "/roles/delete/permissions"
+        };
+    }
+
+    // Deletes roles with their permissions, requires login beforehand.
+    // Requires highest permissions.
+    rpc DeleteRoles(DeleteRolesRequest) returns (DeleteRolesResponse) {
+        option (google.api.http) = {
+            delete: "/roles/delete"
+        };
+    }
 }
 
 enum Status {
@@ -69,12 +125,19 @@ enum Status {
     STATUS_ERROR = 2;
 }
 
-// TODO: add additional data to user enum
-enum User {
-    USER_UNSPECIFIED = 0;
-    USER_NAME = 1;
-    USER_PWD = 2;
-    // ...
+message User {
+    string id = 1;
+    string name = 2;
+    map<string, string> roles = 3; // Key = pnd uuid, value= role name
+    string password = 4;
+    string token = 5;
+}
+
+message Role {
+    string id = 1;
+    string name = 2;
+    string description = 3;
+    repeated string permissions = 4;
 }
 
 // Login
@@ -112,10 +175,21 @@ message CreateUsersResponse {
     Status status = 2;
 }
 
+// GetUser
+message GetUserRequest {
+    int64 timestamp = 1;
+    string name = 2;
+}
+
+message GetUserResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+    User user = 3;
+}
+
 // GetUsers
 message GetUsersRequest {
     int64 timestamp = 1;
-    string token = 2;
 }
 
 message GetUsersResponse {
@@ -145,3 +219,71 @@ message DeleteUsersResponse {
     int64 timestamp = 1;
     Status status = 2;
 }
+
+// CreateRoles
+message CreateRolesRequest {
+    int64 timestamp = 1;
+    repeated Role roles = 2;
+}
+
+message CreateRolesResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
+
+// GetRole
+message GetRoleRequest {
+    int64 timestamp = 1;
+    string role_name = 2;
+}
+
+message GetRoleResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+    Role role = 3;
+}
+
+// GetRoles
+message GetRolesRequest {
+    int64 timestamp = 1;
+}
+
+message GetRolesResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+    repeated Role roles = 3;
+}
+
+// UpdateRoles
+message UpdateRolesRequest {
+    int64 timestamp = 1;
+    repeated Role roles = 2;
+}
+
+message UpdateRolesResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
+
+// DeletePermissionsForRole
+message DeletePermissionsForRoleRequest {
+    int64 timestamp = 1;
+    string role_name = 2;
+    repeated string permissions_to_delete = 3;
+}
+
+message DeletePermissionsForRoleResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
+
+// DeleteRoles
+message DeleteRolesRequest {
+    int64 timestamp = 1;
+    repeated string role_name = 2;
+}
+
+message DeleteRolesResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
diff --git a/cli/adapter/PndAdapter.go b/cli/adapter/PndAdapter.go
index e593d40f47986cb0ac4287f5fbcc6a578b7c7fc2..a03b5480bb0bb6bda6defd5aabacd862fb9a46d2 100644
--- a/cli/adapter/PndAdapter.go
+++ b/cli/adapter/PndAdapter.go
@@ -1,6 +1,8 @@
 package adapter
 
 import (
+	"context"
+
 	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
 	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
 	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
@@ -48,8 +50,8 @@ func (p *PndAdapter) RemoveSbi(uuid.UUID) error {
 
 // AddDevice adds a new device to the controller. The device name is optional.
 // If no name is provided a name will be generated upon device creation.
-func (p *PndAdapter) AddDevice(name string, opts *tpb.TransportOption, sid uuid.UUID) (*ppb.SetOndListResponse, error) {
-	resp, err := api.AddDevice(p.endpoint, name, opts, sid, p.ID())
+func (p *PndAdapter) AddDevice(ctx context.Context, name string, opts *tpb.TransportOption, sid uuid.UUID) (*ppb.SetOndListResponse, error) {
+	resp, err := api.AddDevice(ctx, p.endpoint, name, opts, sid, p.ID())
 	if err != nil {
 		return nil, err
 	}
@@ -57,8 +59,8 @@ func (p *PndAdapter) AddDevice(name string, opts *tpb.TransportOption, sid uuid.
 }
 
 // GetSbiSchemaTree requests a sbi schema tree.
-func (p *PndAdapter) GetSbiSchemaTree(sid uuid.UUID) (map[string]*yang.Entry, error) {
-	resp, err := api.GetSbiSchemaTree(p.Endpoint(), p.ID(), sid)
+func (p *PndAdapter) GetSbiSchemaTree(ctx context.Context, sid uuid.UUID) (map[string]*yang.Entry, error) {
+	resp, err := api.GetSbiSchemaTree(ctx, p.Endpoint(), p.ID(), sid)
 	if err != nil {
 		return nil, err
 	}
@@ -67,8 +69,8 @@ func (p *PndAdapter) GetSbiSchemaTree(sid uuid.UUID) (map[string]*yang.Entry, er
 
 // GetDevice requests one or multiple devices belonging to a given
 // PrincipalNetworkDomain from the controller.
-func (p *PndAdapter) GetDevice(identifier ...string) ([]*ppb.OrchestratedNetworkingDevice, error) {
-	resp, err := api.GetDevice(p.endpoint, p.id.String(), identifier...)
+func (p *PndAdapter) GetDevice(ctx context.Context, identifier ...string) ([]*ppb.OrchestratedNetworkingDevice, error) {
+	resp, err := api.GetDevice(ctx, p.endpoint, p.id.String(), identifier...)
 	if err != nil {
 		return nil, err
 	}
@@ -77,8 +79,8 @@ func (p *PndAdapter) GetDevice(identifier ...string) ([]*ppb.OrchestratedNetwork
 
 // GetDevices requests all devices belonging to the PrincipalNetworkDomain
 // attached to this adapter.
-func (p *PndAdapter) GetDevices() ([]*ppb.OrchestratedNetworkingDevice, error) {
-	resp, err := api.GetDevices(p.endpoint, p.id.String())
+func (p *PndAdapter) GetDevices(ctx context.Context) ([]*ppb.OrchestratedNetworkingDevice, error) {
+	resp, err := api.GetDevices(ctx, p.endpoint, p.id.String())
 	if err != nil {
 		return nil, err
 	}
@@ -86,8 +88,8 @@ func (p *PndAdapter) GetDevices() ([]*ppb.OrchestratedNetworkingDevice, error) {
 }
 
 // RemoveDevice removes a device from the controller
-func (p *PndAdapter) RemoveDevice(did uuid.UUID) (*ppb.DeleteOndResponse, error) {
-	resp, err := api.DeleteDevice(p.endpoint, p.id.String(), did.String())
+func (p *PndAdapter) RemoveDevice(ctx context.Context, did uuid.UUID) (*ppb.DeleteOndResponse, error) {
+	resp, err := api.DeleteDevice(ctx, p.endpoint, p.id.String(), did.String())
 	if err != nil {
 		return nil, err
 	}
@@ -95,8 +97,8 @@ func (p *PndAdapter) RemoveDevice(did uuid.UUID) (*ppb.DeleteOndResponse, error)
 }
 
 // RemovePnd removes a PND from the controller
-func (p *PndAdapter) RemovePnd(pid uuid.UUID) (*core.DeletePndResponse, error) {
-	resp, err := api.DeletePnd(p.endpoint, pid.String())
+func (p *PndAdapter) RemovePnd(ctx context.Context, pid uuid.UUID) (*core.DeletePndResponse, error) {
+	resp, err := api.DeletePnd(ctx, p.endpoint, pid.String())
 	if err != nil {
 		return nil, err
 	}
@@ -106,12 +108,12 @@ func (p *PndAdapter) RemovePnd(pid uuid.UUID) (*core.DeletePndResponse, error) {
 // ChangeOND sends an API call to the controller requesting the creation of
 // a change from the provided Operation, path and value. The Change is marked
 // as Pending and times out after the specified timeout period
-func (p *PndAdapter) ChangeOND(duid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (uuid.UUID, error) {
+func (p *PndAdapter) ChangeOND(ctx context.Context, duid uuid.UUID, operation ppb.ApiOperation, path string, value ...string) (uuid.UUID, error) {
 	var v string
 	if len(value) != 0 {
 		v = value[0]
 	}
-	resp, err := api.ChangeRequest(p.endpoint, duid.String(), p.id.String(), path, v, operation)
+	resp, err := api.ChangeRequest(ctx, p.endpoint, duid.String(), p.id.String(), path, v, operation)
 	if err != nil {
 		return uuid.Nil, err
 	}
@@ -121,8 +123,8 @@ func (p *PndAdapter) ChangeOND(duid uuid.UUID, operation ppb.ApiOperation, path
 
 // Request sends an API call to the controller requesting the specified path
 // for the specified device
-func (p *PndAdapter) Request(did uuid.UUID, path string) (proto.Message, error) {
-	resp, err := api.GetPath(p.endpoint, p.id.String(), did.String(), path)
+func (p *PndAdapter) Request(ctx context.Context, did uuid.UUID, path string) (proto.Message, error) {
+	resp, err := api.GetPath(ctx, p.endpoint, p.id.String(), did.String(), path)
 	if err != nil {
 		return nil, err
 	}
@@ -131,8 +133,8 @@ func (p *PndAdapter) Request(did uuid.UUID, path string) (proto.Message, error)
 
 // RequestAll sends an API call to the controller requesting the specified path
 // for all registered devices. Not yet implemented.
-func (p *PndAdapter) RequestAll(path string) ([]proto.Message, error) {
-	resp, err := api.GetDevices(p.Endpoint(), p.ID().String())
+func (p *PndAdapter) RequestAll(ctx context.Context, path string) ([]proto.Message, error) {
+	resp, err := api.GetDevices(ctx, p.Endpoint(), p.ID().String())
 	if err != nil {
 		return []proto.Message{}, err
 	}
@@ -143,7 +145,7 @@ func (p *PndAdapter) RequestAll(path string) ([]proto.Message, error) {
 		// TODO: probably the controller should do this; this would result in a
 		// single request from CLI side.
 		g.Go(func() error {
-			resp, err := api.GetPath(p.endpoint, p.id.String(), ond.GetId(), path)
+			resp, err := api.GetPath(ctx, p.endpoint, p.id.String(), ond.GetId(), path)
 			if err != nil {
 				return err
 			}
@@ -167,8 +169,8 @@ func (p *PndAdapter) ContainsDevice(uuid.UUID) bool {
 
 // GetSbi sends an API call to the controller requesting the
 // registered SBI with the provided ID.
-func (p *PndAdapter) GetSbi(sid ...string) ([]*spb.SouthboundInterface, error) {
-	resp, err := api.GetSbi(p.endpoint, p.id.String(), sid...)
+func (p *PndAdapter) GetSbi(ctx context.Context, sid ...string) ([]*spb.SouthboundInterface, error) {
+	resp, err := api.GetSbi(ctx, p.endpoint, p.id.String(), sid...)
 	if err != nil {
 		return nil, err
 	}
@@ -177,8 +179,8 @@ func (p *PndAdapter) GetSbi(sid ...string) ([]*spb.SouthboundInterface, error) {
 
 // GetSBIs sends an API call to the controller requesting the
 // registered SBIs. Not implemented, always returns nil
-func (p *PndAdapter) GetSBIs() ([]*spb.SouthboundInterface, error) {
-	resp, err := api.GetSBIs(p.endpoint, p.id.String())
+func (p *PndAdapter) GetSBIs(ctx context.Context) ([]*spb.SouthboundInterface, error) {
+	resp, err := api.GetSBIs(ctx, p.endpoint, p.id.String())
 	if err != nil {
 		return nil, err
 	}
@@ -197,8 +199,8 @@ func (p *PndAdapter) Endpoint() string {
 
 // PendingChanges sends an API call to the controller requesting
 // the UUIDs of all pending changes
-func (p *PndAdapter) PendingChanges() []uuid.UUID {
-	resp, err := api.GetChanges(p.endpoint, p.id.String())
+func (p *PndAdapter) PendingChanges(ctx context.Context) []uuid.UUID {
+	resp, err := api.GetChanges(ctx, p.endpoint, p.id.String())
 	if err != nil {
 		log.Error(err)
 		return nil
@@ -208,8 +210,8 @@ func (p *PndAdapter) PendingChanges() []uuid.UUID {
 
 // CommittedChanges sends an API call to the controller requesting
 // the UUIDs of all committed changes
-func (p *PndAdapter) CommittedChanges() []uuid.UUID {
-	resp, err := api.GetChanges(p.endpoint, p.id.String())
+func (p *PndAdapter) CommittedChanges(ctx context.Context) []uuid.UUID {
+	resp, err := api.GetChanges(ctx, p.endpoint, p.id.String())
 	if err != nil {
 		log.Error(err)
 		return nil
@@ -223,8 +225,8 @@ func (p *PndAdapter) GetChange(uuid.UUID) (change.Change, error) {
 }
 
 // Commit sends an API call to the controller committing the specified change
-func (p *PndAdapter) Commit(cuid uuid.UUID) error {
-	resp, err := api.Commit(p.endpoint, p.id.String(), cuid.String())
+func (p *PndAdapter) Commit(ctx context.Context, cuid uuid.UUID) error {
+	resp, err := api.Commit(ctx, p.endpoint, p.id.String(), cuid.String())
 	if err != nil {
 		return err
 	}
@@ -233,8 +235,8 @@ func (p *PndAdapter) Commit(cuid uuid.UUID) error {
 }
 
 // Confirm sends an API call to the controller confirming the specified change
-func (p *PndAdapter) Confirm(cuid uuid.UUID) error {
-	resp, err := api.Confirm(p.endpoint, p.id.String(), cuid.String())
+func (p *PndAdapter) Confirm(ctx context.Context, cuid uuid.UUID) error {
+	resp, err := api.Confirm(ctx, p.endpoint, p.id.String(), cuid.String())
 	if err != nil {
 		return err
 	}
diff --git a/cli/adapter/PndAdapter_test.go b/cli/adapter/PndAdapter_test.go
index 6cccc35128488a9eb17e605c3958f0b60fa0b50c..2f5b1612abd5b6345387b47cfd8ecf4e2dd7c141 100644
--- a/cli/adapter/PndAdapter_test.go
+++ b/cli/adapter/PndAdapter_test.go
@@ -1,6 +1,7 @@
 package adapter
 
 import (
+	"context"
 	"reflect"
 	"testing"
 
@@ -123,7 +124,7 @@ func TestPndAdapter_AddDevice(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if _, err := p.AddDevice(tt.args.name, tt.args.opts, tt.args.sid); (err != nil) != tt.wantErr {
+			if _, err := p.AddDevice(context.TODO(), tt.args.name, tt.args.opts, tt.args.sid); (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.AddDevice() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
@@ -153,7 +154,7 @@ func TestPndAdapter_GetDevice(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			got, err := p.GetDevice(tt.args.identifier)
+			got, err := p.GetDevice(context.TODO(), tt.args.identifier)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.GetDevice() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -187,7 +188,7 @@ func TestPndAdapter_RemoveDevice(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if _, err := p.RemoveDevice(tt.args.did); (err != nil) != tt.wantErr {
+			if _, err := p.RemoveDevice(context.TODO(), tt.args.did); (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.RemoveDevice() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
@@ -219,7 +220,7 @@ func TestPndAdapter_ChangeOND(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			_, err := p.ChangeOND(tt.args.uuid, tt.args.operation, tt.args.path, tt.args.value...)
+			_, err := p.ChangeOND(context.TODO(), tt.args.uuid, tt.args.operation, tt.args.path, tt.args.value...)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.ChangeOND() error = %v, wantErr %v", err, tt.wantErr)
 			}
@@ -250,7 +251,7 @@ func TestPndAdapter_Request(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			_, err := p.Request(tt.args.did, tt.args.path)
+			_, err := p.Request(context.TODO(), tt.args.did, tt.args.path)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.Request() error = %v, wantErr %v", err, tt.wantErr)
 			}
@@ -280,7 +281,7 @@ func TestPndAdapter_RequestAll(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if _, err := p.RequestAll(tt.args.in0); (err != nil) != tt.wantErr {
+			if _, err := p.RequestAll(context.TODO(), tt.args.in0); (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.RequestAll() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
@@ -334,7 +335,7 @@ func TestPndAdapter_GetSBIs(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if got, _ := p.GetSBIs(); !reflect.DeepEqual(got, tt.want) {
+			if got, _ := p.GetSBIs(context.TODO()); !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("PndAdapter.GetSBIs() = %v, want %v", got, tt.want)
 			}
 		})
@@ -384,7 +385,7 @@ func TestPndAdapter_PendingChanges(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if got := p.PendingChanges(); !reflect.DeepEqual(got, tt.want) {
+			if got := p.PendingChanges(context.TODO()); !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("PndAdapter.PendingChanges() = %v, want %v", got, tt.want)
 			}
 		})
@@ -409,7 +410,7 @@ func TestPndAdapter_CommittedChanges(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if got := p.CommittedChanges(); !reflect.DeepEqual(got, tt.want) {
+			if got := p.CommittedChanges(context.TODO()); !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("PndAdapter.CommittedChanges() = %v, want %v", got, tt.want)
 			}
 		})
@@ -473,7 +474,7 @@ func TestPndAdapter_Commit(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if err := p.Commit(tt.args.cuid); (err != nil) != tt.wantErr {
+			if err := p.Commit(context.TODO(), tt.args.cuid); (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.Commit() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
@@ -502,7 +503,7 @@ func TestPndAdapter_Confirm(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if err := p.Confirm(tt.args.cuid); (err != nil) != tt.wantErr {
+			if err := p.Confirm(context.TODO(), tt.args.cuid); (err != nil) != tt.wantErr {
 				t.Errorf("PndAdapter.Confirm() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
diff --git a/cli/cmd/changeCommit.go b/cli/cmd/changeCommit.go
index 49798b98dc266d0b7e22cc62c8558ba5a4428198..8bdc0ea4190d9acf8a47492bcb975566becda5f9 100644
--- a/cli/cmd/changeCommit.go
+++ b/cli/cmd/changeCommit.go
@@ -50,7 +50,7 @@ Change UUID must be specified as positional argument.`,
 		if err != nil {
 			log.Fatal(err)
 		}
-		if err := pndAdapter.Commit(cuid); err != nil {
+		if err := pndAdapter.Commit(createContextWithAuthorization(), cuid); err != nil {
 			log.Fatal(err)
 		}
 	},
diff --git a/cli/cmd/changeConfirm.go b/cli/cmd/changeConfirm.go
index 3818fc4eaf8cd2560995518d7a3ac646894c11dd..4fa511580fcd55858702c74fb1bf2e0389498b07 100644
--- a/cli/cmd/changeConfirm.go
+++ b/cli/cmd/changeConfirm.go
@@ -50,7 +50,7 @@ Change UUID must be specified as positional argument`,
 		if err != nil {
 			log.Fatal(err)
 		}
-		if err := pndAdapter.Confirm(cuid); err != nil {
+		if err := pndAdapter.Confirm(createContextWithAuthorization(), cuid); err != nil {
 			log.Fatal(err)
 		}
 	},
diff --git a/cli/cmd/changeList.go b/cli/cmd/changeList.go
index f11a6274a2f3c5c92f9124ed50af66587793933d..dcd3dc4f4e37a3e480a1a8d7feeacdb673a17802 100644
--- a/cli/cmd/changeList.go
+++ b/cli/cmd/changeList.go
@@ -44,12 +44,12 @@ var changeListCmd = &cobra.Command{
 	Long:    `Lists all configuration changes with their UUIDs.`,
 
 	Run: func(cmd *cobra.Command, args []string) {
-		committed := pndAdapter.CommittedChanges()
+		committed := pndAdapter.CommittedChanges(createContextWithAuthorization())
 		log.Info("committed changes:")
 		for i, ch := range committed {
 			log.Infof("    Change %v: %v", i+1, ch.String())
 		}
-		pending := pndAdapter.PendingChanges()
+		pending := pndAdapter.PendingChanges(createContextWithAuthorization())
 		log.Info("pending changes:")
 		for i, ch := range pending {
 			log.Infof("    Change %v: %v", i+1, ch.String())
diff --git a/cli/cmd/deviceCreate.go b/cli/cmd/deviceCreate.go
index 86b4307889882ea067d08ab093192202ddbd560d..56781bf23682bf5c68c2078216d8f04c1d91d60d 100644
--- a/cli/cmd/deviceCreate.go
+++ b/cli/cmd/deviceCreate.go
@@ -76,7 +76,7 @@ if they diverge from the default credentials (user:'admin' and pw:'arista').`,
 			return err
 		}
 
-		resp, err := pndAdapter.AddDevice(deviceName, opt, sid)
+		resp, err := pndAdapter.AddDevice(createContextWithAuthorization(), deviceName, opt, sid)
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/deviceDelete.go b/cli/cmd/deviceDelete.go
index 1dc333899aa5de5740e9e99515db96df64c43e85..82769ea903f055eed956ee4ab75f73f58b249e3b 100644
--- a/cli/cmd/deviceDelete.go
+++ b/cli/cmd/deviceDelete.go
@@ -52,6 +52,7 @@ The device UUID and request path must be specified as a positional arguments.`,
 			log.Fatal(err)
 		}
 		log.Info(pndAdapter.ChangeOND(
+			createContextWithAuthorization(),
 			did,
 			ppb.ApiOperation_API_OPERATION_DELETE,
 			args[1],
diff --git a/cli/cmd/deviceGet.go b/cli/cmd/deviceGet.go
index feb2c5c2d1c392d048237fce68fdfe18fc892758..0bbaaff0b4f9a0435fd4329028ff3bed9cd34bca 100644
--- a/cli/cmd/deviceGet.go
+++ b/cli/cmd/deviceGet.go
@@ -53,6 +53,7 @@ The device UUID and request path must be specified as a positional arguments.`,
 		}
 
 		message, err := pndAdapter.Request(
+			createContextWithAuthorization(),
 			did,
 			args[1],
 		)
diff --git a/cli/cmd/deviceList.go b/cli/cmd/deviceList.go
index 3ad626279bd82a45294ab36d1c30343d7848ed6a..3e0d7408fd8067b9351eb4166eb6e6d3485ca25c 100644
--- a/cli/cmd/deviceList.go
+++ b/cli/cmd/deviceList.go
@@ -46,7 +46,7 @@ var deviceListCmd = &cobra.Command{
 	Long:    "List all orchestrated network devices within the current PND.",
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		respONDs, err := pndAdapter.GetDevices()
+		respONDs, err := pndAdapter.GetDevices(createContextWithAuthorization())
 		if err != nil {
 			return err
 		}
@@ -57,7 +57,7 @@ var deviceListCmd = &cobra.Command{
 			if err != nil {
 				return err
 			}
-			tree, err := pndAdapter.GetSbiSchemaTree(sid)
+			tree, err := pndAdapter.GetSbiSchemaTree(createContextWithAuthorization(), sid)
 			if err != nil {
 				return err
 			}
diff --git a/cli/cmd/deviceRemove.go b/cli/cmd/deviceRemove.go
index a076819b0c7ec4641a966ed01415c6852d702c27..37170c2de6127f7fe7cbc97705a87525dec29dfb 100644
--- a/cli/cmd/deviceRemove.go
+++ b/cli/cmd/deviceRemove.go
@@ -52,7 +52,7 @@ The device UUID must be specified as a positional argument.`,
 		if err != nil {
 			return err
 		}
-		resp, err := pndAdapter.RemoveDevice(did)
+		resp, err := pndAdapter.RemoveDevice(createContextWithAuthorization(), did)
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/deviceSet.go b/cli/cmd/deviceSet.go
index 257b0e831eb59e88c87754c3ceba73619f832504..a8088d34a9e67c18a23d96e971b26198eaa64d55 100644
--- a/cli/cmd/deviceSet.go
+++ b/cli/cmd/deviceSet.go
@@ -65,6 +65,7 @@ To enable replacing behaviour (destructive!), set the --replace flag."`,
 			log.Info("Update")
 		}
 		log.Info(pndAdapter.ChangeOND(
+			createContextWithAuthorization(),
 			did,
 			operation,
 			args[1],
diff --git a/cli/cmd/deviceShow.go b/cli/cmd/deviceShow.go
index d6d4953171561209b64df57bc8fe66bb8a91c651..7fb4198839ae0d72ce2995221af22efa315e5ef6 100644
--- a/cli/cmd/deviceShow.go
+++ b/cli/cmd/deviceShow.go
@@ -47,7 +47,7 @@ The device information returned is the information as currently stored in the co
 The actual device is not queried directly.`,
 
 	Run: func(cmd *cobra.Command, args []string) {
-		log.Info(pndAdapter.GetDevice(args[0]))
+		log.Info(pndAdapter.GetDevice(createContextWithAuthorization(), args[0]))
 	},
 }
 
diff --git a/cli/cmd/init.go b/cli/cmd/init.go
index b23c67c467ef44a69612a2e5e14880b909fdb5b3..f2d809e8744a915d4c8c674c3a4abcc199fbf5c6 100644
--- a/cli/cmd/init.go
+++ b/cli/cmd/init.go
@@ -35,7 +35,6 @@ import (
 	"fmt"
 	"os"
 
-	"code.fbi.h-da.de/danet/gosdn/controller/api"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
@@ -51,17 +50,7 @@ var initCmd = &cobra.Command{
 The --controller flag is required to change the controller address`,
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		if err := api.Init(viper.GetString("controllerAPIEndpoint")); err != nil {
-			return err
-		}
-		resp, err := api.GetSBIs(viper.GetString("controllerAPIEndpoint"), viper.GetString("CLI_PND"))
-		if err != nil {
-			return err
-		}
-
-		sid := resp.Sbi[0].GetId()
-		viper.Set("CLI_SBI", sid)
-		log.Infof("SBI: %v", sid)
+		log.Infof("New controller address: %v", viper.GetString("controllerAPIEndpoint"))
 
 		if err := viper.WriteConfig(); err != nil {
 			fmt.Fprintln(os.Stderr, "Could not write config:", err)
diff --git a/cli/cmd/list.go b/cli/cmd/list.go
index 27fcf31350290e1a3723906794abd2022fbbd952..f8d8bbb5a693bb0d4044f3075599404d52ac154e 100644
--- a/cli/cmd/list.go
+++ b/cli/cmd/list.go
@@ -49,12 +49,12 @@ var listCmd = &cobra.Command{
 
 	RunE: func(cmd *cobra.Command, args []string) error {
 		addr := viper.GetString("controllerApiEndpoint")
-		resp, err := api.GetIds(addr)
+		resp, err := api.GetIds(createContextWithAuthorization(), addr)
 		if err != nil {
 			return err
 		}
 		for i, pnd := range resp {
-			ondResp, err := api.GetDevices(addr, pnd.GetId())
+			ondResp, err := api.GetDevices(createContextWithAuthorization(), addr, pnd.GetId())
 			if err != nil {
 				return err
 			}
diff --git a/cli/cmd/login.go b/cli/cmd/login.go
index d8a1891ecb03e9bdc1b328e73cec10328f575e58..3d9aba572e887642340628b92979cffec0662dcf 100644
--- a/cli/cmd/login.go
+++ b/cli/cmd/login.go
@@ -32,6 +32,8 @@ POSSIBILITY OF SUCH DAMAGE.
 package cmd
 
 import (
+	"context"
+
 	"code.fbi.h-da.de/danet/gosdn/controller/api"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
@@ -46,33 +48,36 @@ var loginCmd = &cobra.Command{
     User credentials need to be provided in the body`,
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		resp, err := api.Login(viper.GetString("controllerAPIEndpoint"), nbUser, nbUserPwd)
+		// TODO: maybe add credentials in context instead of context.TODO()
+		resp, err := api.Login(context.TODO(), viper.GetString("controllerAPIEndpoint"), nbUserName, nbUserPwd)
 		if err != nil {
 			return err
 		}
 
-		// TODO: set token here
-		log.Info("LoginResponse: " + resp.Token)
+		userToken = resp.Token
+
+		if err := api.Init(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint")); err != nil {
+			return err
+		}
+
+		sbiResp, err := api.GetSBIs(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), viper.GetString("CLI_PND"))
+		if err != nil {
+			return err
+		}
+		sid := sbiResp.Sbi[0].GetId()
+		viper.Set("CLI_SBI", sid)
+		log.Infof("SBI: %v", sid)
 
 		return nil
 	},
 }
 
-var nbUser string
+var nbUserName string
 var nbUserPwd string
 
 func init() {
 	rootCmd.AddCommand(loginCmd)
 
-	// Here you will define your flags and configuration settings.
-
-	// Cobra supports Persistent Flags which will work for this command
-	// and all subcommands, e.g.:
-	// loginCmd.PersistentFlags().String("foo", "", "A help for foo")
-
-	// Cobra supports local flags which will only run when this command
-	// is called directly, e.g.:
-	// loginCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
-	loginCmd.Flags().StringVar(&nbUser, "u", "", "username for login")
+	loginCmd.Flags().StringVar(&nbUserName, "u", "", "username for login")
 	loginCmd.Flags().StringVar(&nbUserPwd, "p", "", "pwd for login")
 }
diff --git a/cli/cmd/logout.go b/cli/cmd/logout.go
index 3fd3ac068767a1e498e7b8ac1a2aba64f04eadf4..a797c80e58d29b8981fd69f83d2bd120a1c980c5 100644
--- a/cli/cmd/logout.go
+++ b/cli/cmd/logout.go
@@ -45,7 +45,7 @@ var logoutCmd = &cobra.Command{
 	Long:  `Logs the current user out. Further actions on the controller are not permitted after this.`,
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		resp, err := api.Logout(viper.GetString("controllerAPIEndpoint"), nbUser)
+		resp, err := api.Logout(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), nbUserName)
 		if err != nil {
 			return err
 		}
@@ -60,14 +60,5 @@ var logoutCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(logoutCmd)
 
-	// Here you will define your flags and configuration settings.
-
-	// Cobra supports Persistent Flags which will work for this command
-	// and all subcommands, e.g.:
-	// loginCmd.PersistentFlags().String("foo", "", "A help for foo")
-
-	// Cobra supports local flags which will only run when this command
-	// is called directly, e.g.:
-	// loginCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
-	logoutCmd.Flags().StringVar(&nbUser, "u", "", "username for logout")
+	logoutCmd.Flags().StringVar(&nbUserName, "u", "", "username for logout")
 }
diff --git a/cli/cmd/pndCreate.go b/cli/cmd/pndCreate.go
index 9a0edf3ee706be42dc56f7775c59d87524dd7627..f08d429d2685139006200e9ed21949baf79d9d7f 100644
--- a/cli/cmd/pndCreate.go
+++ b/cli/cmd/pndCreate.go
@@ -52,7 +52,7 @@ A description must be passed as positional argument. A name and default SBI can
 passed using parameters.`,
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		resp, err := api.AddPnd(viper.GetString("controllerApiEndpoint"), pndName, args[0], pndDefaultSbi)
+		resp, err := api.AddPnd(createContextWithAuthorization(), viper.GetString("controllerApiEndpoint"), pndName, args[0], pndDefaultSbi)
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/pndGet.go b/cli/cmd/pndGet.go
index ce8016a957fc2ab3a9596db1581008d3f76b73de..ef5e36a32d68e9f149ca293ab10a6b51dfce03d8 100644
--- a/cli/cmd/pndGet.go
+++ b/cli/cmd/pndGet.go
@@ -44,7 +44,7 @@ var pndGetCmd = &cobra.Command{
 	Long:  `Get one or multiple PNDs by uuid or name and print them to stdout.`,
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		resp, err := api.GetPnd(viper.GetString("controllerApiEndpoint"), args...)
+		resp, err := api.GetPnd(createContextWithAuthorization(), viper.GetString("controllerApiEndpoint"), args...)
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/pndList.go b/cli/cmd/pndList.go
index aa6608cb17aef5c960e30719e97f316fa13ccbf5..2f44d74b661e3df3ab5593eb7d2d93d05dcb6868 100644
--- a/cli/cmd/pndList.go
+++ b/cli/cmd/pndList.go
@@ -46,7 +46,8 @@ var pndListCmd = &cobra.Command{
 	Long:    `List all information about the current PND.`,
 
 	RunE: func(cmd *cobra.Command, args []string) error {
-		resp, err := api.GetPnd(pndAdapter.Endpoint(), pndAdapter.ID().String())
+
+		resp, err := api.GetPnd(createContextWithAuthorization(), pndAdapter.Endpoint(), pndAdapter.ID().String())
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/pndRemove.go b/cli/cmd/pndRemove.go
index 6ff42079f7f3c69b273a1ee7461ea4e5041188b2..ed4890a7581d014c938eef65a7c5db2f4cd3b03d 100644
--- a/cli/cmd/pndRemove.go
+++ b/cli/cmd/pndRemove.go
@@ -50,7 +50,7 @@ var pndRemoveCmd = &cobra.Command{
 		if err != nil {
 			return err
 		}
-		resp, err := pndAdapter.RemovePnd(pid)
+		resp, err := pndAdapter.RemovePnd(createContextWithAuthorization(), pid)
 		if err != nil {
 			return err
 		}
diff --git a/cli/cmd/pndUse.go b/cli/cmd/pndUse.go
index 5b3c345297ad9f5a2bc05b97eb02e31841e4cfef..22be64cab7dfa2a0402b4c1b828a36c1f307e4ac 100644
--- a/cli/cmd/pndUse.go
+++ b/cli/cmd/pndUse.go
@@ -55,7 +55,7 @@ var pndUseCmd = &cobra.Command{
 			return err
 		}
 
-		_, err = api.GetPnd(viper.GetString("controllerAPIEndpoint"), newPND)
+		_, err = api.GetPnd(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), newPND)
 		if err != nil {
 			log.Fatal(err)
 			return err
diff --git a/cli/cmd/prompt.go b/cli/cmd/prompt.go
index f4828889d8ec806ac12891a4a3f62aa91d68385d..e9704be94b631c7c17c75e8fa744f8c0ef8ae7f8 100644
--- a/cli/cmd/prompt.go
+++ b/cli/cmd/prompt.go
@@ -131,7 +131,7 @@ func deviceGetCompletion(c *PromptCompleter, d prompt.Document, inputSplit []str
 			if c, ok := c.yangSchemaCompleterMap[id]; ok {
 				return c.Complete(d)
 			}
-			dev, err := pndAdapter.GetDevice(id.String())
+			dev, err := pndAdapter.GetDevice(createContextWithAuthorization(), id.String())
 			if err != nil {
 				return []prompt.Suggest{}
 			}
@@ -139,7 +139,7 @@ func deviceGetCompletion(c *PromptCompleter, d prompt.Document, inputSplit []str
 			if err != nil {
 				return []prompt.Suggest{}
 			}
-			schemaTree, err := pndAdapter.GetSbiSchemaTree(sid)
+			schemaTree, err := pndAdapter.GetSbiSchemaTree(createContextWithAuthorization(), sid)
 			if err != nil {
 				return []prompt.Suggest{}
 			}
diff --git a/cli/cmd/userCreate.go b/cli/cmd/userCreate.go
new file mode 100644
index 0000000000000000000000000000000000000000..0b64cad0f6d8c2401582ad7b1bc7fc228038db6b
--- /dev/null
+++ b/cli/cmd/userCreate.go
@@ -0,0 +1,85 @@
+/*
+Copyright © 2021 da/net Research Group <danet@h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/api"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+// loginCmd represents the login command
+var userCreateCmd = &cobra.Command{
+	Use:   "userCreate",
+	Short: "Creates a user with provided data",
+	Long: `Creates a user with provided data.
+    User name and password hashed with (add hash method here!) required,
+    role is optional but needed to operate on PNDs.`,
+
+	RunE: func(cmd *cobra.Command, args []string) error {
+
+		roles := map[string]string{}
+		// only active pnd for now, add option for additional param later
+		roles[viper.GetString("CLI_PND")] = nbUserRole
+
+		//TODO(faseid): hash password
+
+		// only one for now, add option to add more users at once later
+		users := []*apb.User{
+			{
+				Name:     nbUserName,
+				Password: nbUserPwd,
+				Roles:    roles,
+			},
+		}
+
+		resp, err := api.CreateUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), users)
+		if err != nil {
+			return err
+		}
+		log.Infof("Users created: %v", resp.Status)
+
+		return nil
+	},
+}
+
+var nbUserRole string
+
+func init() {
+	rootCmd.AddCommand(userCreateCmd)
+
+	userCreateCmd.Flags().StringVar(&nbUserName, "u", "", "username for login")
+	userCreateCmd.Flags().StringVar(&nbUserPwd, "p", "", "pwd for login")
+	userCreateCmd.Flags().StringVar(&nbUserRole, "r", "", "role for user")
+}
diff --git a/cli/cmd/userDelete.go b/cli/cmd/userDelete.go
new file mode 100644
index 0000000000000000000000000000000000000000..c8bfb21519f4f5f303ab5ec43a0c5b880437e090
--- /dev/null
+++ b/cli/cmd/userDelete.go
@@ -0,0 +1,67 @@
+/*
+Copyright © 2021 da/net Research Group <danet@h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/api"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+// loginCmd represents the login command
+var userDeleteCmd = &cobra.Command{
+	Use:   "userDelete",
+	Short: "Deletes a user with provided data",
+	Long: `Deletes a user with provided data.
+    Requires the user name of the user which should be deleted.`,
+
+	RunE: func(cmd *cobra.Command, args []string) error {
+
+		// only one user for now, add more later if needed
+		users := []string{nbUserName}
+
+		resp, err := api.DeleteUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), users)
+		if err != nil {
+			return err
+		}
+		log.Infof("Users deleted: %v", resp.Status)
+
+		return nil
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(userDeleteCmd)
+
+	userDeleteCmd.Flags().StringVar(&nbUserName, "u", "", "username to delete")
+}
diff --git a/cli/cmd/userGet.go b/cli/cmd/userGet.go
new file mode 100644
index 0000000000000000000000000000000000000000..b5f1a0bd9ba21765a81df889fabc182029126615
--- /dev/null
+++ b/cli/cmd/userGet.go
@@ -0,0 +1,66 @@
+/*
+Copyright © 2021 da/net Research Group <danet@h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/api"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+// loginCmd represents the login command
+var userGetCmd = &cobra.Command{
+	Use:   "userGet",
+	Short: "Requests one user",
+	Long:  `Requests one user using the provided name to search for it in the stored users.`,
+
+	RunE: func(cmd *cobra.Command, args []string) error {
+		resp, err := api.GetUser(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), nbUserName)
+		if err != nil {
+			return err
+		}
+
+		log.Infof("ID: %v, Name: %v \n", resp.User.Id, resp.User.Name)
+		for key, elem := range resp.User.Roles {
+			log.Infof("Role on PND: %v %v \n", key, elem)
+		}
+
+		return nil
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(userGetCmd)
+
+	userGetCmd.Flags().StringVar(&nbUserName, "u", "", "username to find")
+}
diff --git a/cli/cmd/userGetAll.go b/cli/cmd/userGetAll.go
new file mode 100644
index 0000000000000000000000000000000000000000..e0719ef78706ee47a1c2679fa812ad73cca7843e
--- /dev/null
+++ b/cli/cmd/userGetAll.go
@@ -0,0 +1,66 @@
+/*
+Copyright © 2021 da/net Research Group <danet@h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/api"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+// loginCmd represents the login command
+var userGetAllCmd = &cobra.Command{
+	Use:   "userGetAll",
+	Short: "Requests all the available users",
+	Long:  `Requests all the available users.`,
+
+	RunE: func(cmd *cobra.Command, args []string) error {
+		resp, err := api.GetAllUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"))
+		if err != nil {
+			return err
+		}
+
+		for i, u := range resp.User {
+			log.Infof("User %v: ID: %v, Name: %v \n", i+1, u.Id, u.Name)
+			for key, elem := range u.Roles {
+				log.Infof("Role on PND: %v %v \n", key, elem)
+			}
+		}
+
+		return nil
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(userGetAllCmd)
+}
diff --git a/cli/cmd/userUpdate.go b/cli/cmd/userUpdate.go
new file mode 100644
index 0000000000000000000000000000000000000000..a85365a5bdf43a6e81421f552b3a0a4dc6ffa33f
--- /dev/null
+++ b/cli/cmd/userUpdate.go
@@ -0,0 +1,87 @@
+/*
+Copyright © 2021 da/net Research Group <danet@h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/api"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+// loginCmd represents the login command
+var userUpdateCmd = &cobra.Command{
+	Use:   "userUpdate",
+	Short: "Updates a user with provided data",
+	Long: `Updates a user with provided data.
+    User name and password hashed with (add hash method here!) required,
+    role is optional but needed to operate on PNDs.`,
+
+	RunE: func(cmd *cobra.Command, args []string) error {
+
+		roles := map[string]string{}
+		// only active pnd for now, add option for additional param later
+		roles[viper.GetString("CLI_PND")] = nbUserRole
+
+		//TODO(faseid): hash password
+
+		// only one for now, add option to update more users at once later
+		users := []*apb.User{
+			{
+				Id:       nbUserID,
+				Name:     nbUserName,
+				Password: nbUserPwd,
+				Roles:    roles,
+			},
+		}
+
+		resp, err := api.UpdateUsers(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), users)
+		if err != nil {
+			return err
+		}
+		log.Infof("Users updated: %v", resp.Status)
+
+		return nil
+	},
+}
+
+var nbUserID string
+
+func init() {
+	rootCmd.AddCommand(userUpdateCmd)
+
+	userUpdateCmd.Flags().StringVar(&nbUserID, "i", "", "id of the user")
+	userUpdateCmd.Flags().StringVar(&nbUserName, "u", "", "username for login")
+	userUpdateCmd.Flags().StringVar(&nbUserPwd, "p", "", "pwd for login")
+	userUpdateCmd.Flags().StringVar(&nbUserRole, "r", "", "role for user")
+}
diff --git a/cli/cmd/utils.go b/cli/cmd/utils.go
index 05a538af1c15159cf3189208b5493249611dc8b1..cf6a0378032e6d3305564603ffb893089c2541d0 100644
--- a/cli/cmd/utils.go
+++ b/cli/cmd/utils.go
@@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
 package cmd
 
 import (
+	"context"
 	"errors"
 	"net"
 
@@ -40,8 +41,11 @@ import (
 	"code.fbi.h-da.de/danet/gosdn/controller/api"
 	"github.com/c-bata/go-prompt"
 	"github.com/spf13/viper"
+	"google.golang.org/grpc/metadata"
 )
 
+var userToken string
+
 func checkIPPort(string) error {
 	// check if address is in the format <IP>:<port>
 	ip, _, err := net.SplitHostPort(address)
@@ -57,7 +61,7 @@ func checkIPPort(string) error {
 }
 
 func getDevices() []prompt.Suggest {
-	resp, err := api.GetDevices(pndAdapter.Endpoint(), pndAdapter.ID().String())
+	resp, err := api.GetDevices(createContextWithAuthorization(), pndAdapter.Endpoint(), pndAdapter.ID().String())
 	if err != nil {
 		return []prompt.Suggest{}
 	}
@@ -70,7 +74,7 @@ func getDevices() []prompt.Suggest {
 }
 
 func getPnds() []prompt.Suggest {
-	resp, err := api.GetIds(viper.GetString("controllerAPIEndpoint"))
+	resp, err := api.GetIds(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"))
 	if err != nil {
 		return []prompt.Suggest{}
 	}
@@ -83,7 +87,7 @@ func getPnds() []prompt.Suggest {
 }
 
 func getChangesByType(cType pnd.ChangeState) []prompt.Suggest {
-	resp, err := api.GetChanges(pndAdapter.Endpoint(), pndAdapter.ID().String())
+	resp, err := api.GetChanges(createContextWithAuthorization(), pndAdapter.Endpoint(), pndAdapter.ID().String())
 	if err != nil {
 		return []prompt.Suggest{}
 	}
@@ -96,3 +100,9 @@ func getChangesByType(cType pnd.ChangeState) []prompt.Suggest {
 	}
 	return completer.SortSuggestionByText(s)
 }
+
+func createContextWithAuthorization() context.Context {
+	//TODO: try to get token string first, if "" return err, followed by print in cli about required login
+	md := metadata.Pairs("authorize", userToken)
+	return metadata.NewOutgoingContext(context.Background(), md)
+}
diff --git a/controller/api/apiIntegration_test.go b/controller/api/apiIntegration_test.go
index 761b3577c2b9205dba7878e2791515a75fe18cd4..8a288847339ff1ff24aa6388d0cad891b469de3c 100644
--- a/controller/api/apiIntegration_test.go
+++ b/controller/api/apiIntegration_test.go
@@ -1,6 +1,7 @@
 package api
 
 import (
+	"context"
 	"testing"
 
 	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
@@ -29,7 +30,7 @@ func TestApiIntegration(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			defer viper.Reset()
-			if err := Init(testAPIEndpoint); (err != nil) != tt.wantErr {
+			if err := Init(context.TODO(), testAPIEndpoint); (err != nil) != tt.wantErr {
 				switch err.(type) {
 				case viper.ConfigFileNotFoundError:
 				default:
@@ -58,6 +59,7 @@ func TestApiIntegration(t *testing.T) {
 				},
 			}
 			if _, err := AddDevice(
+				context.TODO(),
 				testAPIEndpoint,
 				"test-device",
 				opt,
@@ -70,6 +72,7 @@ func TestApiIntegration(t *testing.T) {
 			did := viper.GetString("LAST_DEVICE_UUID")
 
 			_, err = GetDevice(
+				context.TODO(),
 				testAPIEndpoint,
 				cliPnd,
 				did,
@@ -80,6 +83,7 @@ func TestApiIntegration(t *testing.T) {
 			}
 
 			_, err = GetDevice(
+				context.TODO(),
 				testAPIEndpoint,
 				cliPnd,
 				"",
@@ -92,6 +96,7 @@ func TestApiIntegration(t *testing.T) {
 
 			hostname := guuid.New().String()
 			_, err = ChangeRequest(
+				context.TODO(),
 				testAPIEndpoint,
 				did,
 				cliPnd,
@@ -104,7 +109,7 @@ func TestApiIntegration(t *testing.T) {
 				return
 			}
 
-			resp, err := GetDevice(testAddress, testUsername, testPassword, testPath)
+			resp, err := GetDevice(context.TODO(), testAddress, testUsername, testPassword, testPath)
 			if err != nil {
 				if !tt.wantErr {
 					t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/controller/api/api_test.go b/controller/api/api_test.go
index c7ccbc4e91d32c0b44cee6273af0d0ed61f2a4ff..0f4f904af2aa7dedc1a393879e137cccf037145e 100644
--- a/controller/api/api_test.go
+++ b/controller/api/api_test.go
@@ -1,6 +1,7 @@
 package api
 
 import (
+	"context"
 	"testing"
 
 	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
@@ -13,13 +14,13 @@ import (
 
 func Test_Init(t *testing.T) {
 	viper.SetConfigFile("./api_test.toml")
-	if err := Init(bufnet); err != nil {
+	if err := Init(context.TODO(), bufnet); err != nil {
 		t.Error(err)
 	}
 }
 
 func Test_GetIds(t *testing.T) {
-	resp, err := GetIds(bufnet)
+	resp, err := GetIds(context.TODO(), bufnet)
 	if err != nil {
 		t.Error(err)
 		return
@@ -32,7 +33,7 @@ func Test_AddPnd(t *testing.T) {
 	if err != nil {
 		t.Errorf("AddPnd() error = %v", err)
 	}
-	resp, err := AddPnd(bufnet, "test", "test pnd", sbi.ID().String())
+	resp, err := AddPnd(context.TODO(), bufnet, "test", "test pnd", sbi.ID().String())
 	if err != nil {
 		t.Error(err)
 		return
@@ -41,7 +42,7 @@ func Test_AddPnd(t *testing.T) {
 }
 
 func Test_GetPnd(t *testing.T) {
-	resp, err := GetPnd(bufnet, pndID)
+	resp, err := GetPnd(context.TODO(), bufnet, pndID)
 	if err != nil {
 		t.Error(err)
 		return
@@ -53,7 +54,7 @@ func Test_GetPnd(t *testing.T) {
 }
 
 func Test_GetChanges(t *testing.T) {
-	resp, err := GetChanges(bufnet, pndID)
+	resp, err := GetChanges(context.TODO(), bufnet, pndID)
 	if err != nil {
 		t.Error(err)
 		return
@@ -62,14 +63,14 @@ func Test_GetChanges(t *testing.T) {
 }
 
 func Test_CommitConfirm(t *testing.T) {
-	resp, err := Commit(bufnet, pndID, changeID)
+	resp, err := Commit(context.TODO(), bufnet, pndID, changeID)
 	if err != nil {
 		t.Error(err)
 		return
 	}
 	log.Info(resp)
 
-	resp, err = Confirm(bufnet, pndID, changeID)
+	resp, err = Confirm(context.TODO(), bufnet, pndID, changeID)
 	if err != nil {
 		t.Error(err)
 		return
@@ -86,7 +87,7 @@ func Test_AddDevice(t *testing.T) {
 			GnmiTransportOption: &tpb.GnmiTransportOption{},
 		},
 	}
-	resp, err := AddDevice(bufnet, "test", opt, sbiUUID, pndUUID)
+	resp, err := AddDevice(context.TODO(), bufnet, "test", opt, sbiUUID, pndUUID)
 	if err != nil {
 		t.Error(err)
 		return
@@ -95,7 +96,7 @@ func Test_AddDevice(t *testing.T) {
 }
 
 func Test_GetDevice(t *testing.T) {
-	resp, err := GetDevice(bufnet, pndID, ondID)
+	resp, err := GetDevice(context.TODO(), bufnet, pndID, ondID)
 	if err != nil {
 		t.Error(err)
 		return
@@ -107,7 +108,7 @@ func Test_GetDevice(t *testing.T) {
 }
 
 func Test_Update(t *testing.T) {
-	resp, err := ChangeRequest(bufnet, ondID, pndID, "", "", ppb.ApiOperation_API_OPERATION_UPDATE)
+	resp, err := ChangeRequest(context.TODO(), bufnet, ondID, pndID, "", "", ppb.ApiOperation_API_OPERATION_UPDATE)
 	if err != nil {
 		t.Error(err)
 		return
@@ -116,7 +117,7 @@ func Test_Update(t *testing.T) {
 }
 
 func Test_Replace(t *testing.T) {
-	resp, err := ChangeRequest(bufnet, ondID, pndID, "", "", ppb.ApiOperation_API_OPERATION_REPLACE)
+	resp, err := ChangeRequest(context.TODO(), bufnet, ondID, pndID, "", "", ppb.ApiOperation_API_OPERATION_REPLACE)
 	if err != nil {
 		t.Error(err)
 		return
@@ -125,7 +126,7 @@ func Test_Replace(t *testing.T) {
 }
 
 func Test_Delete(t *testing.T) {
-	resp, err := ChangeRequest(bufnet, ondID, pndID, "", "", ppb.ApiOperation_API_OPERATION_DELETE)
+	resp, err := ChangeRequest(context.TODO(), bufnet, ondID, pndID, "", "", ppb.ApiOperation_API_OPERATION_DELETE)
 	if err != nil {
 		t.Error(err)
 		return
diff --git a/controller/api/grpc.go b/controller/api/grpc.go
index 71f08e6de0c6cf25d7ef1744bb02622ce9e26af9..dc1105b5f15d05b24d515c376f9433b9a12976d0 100644
--- a/controller/api/grpc.go
+++ b/controller/api/grpc.go
@@ -31,28 +31,23 @@ func init() {
 }
 
 // Init initialises the CLI client.
-func Init(addr string) error {
-	ctx := context.Background()
+func Init(ctx context.Context, addr string) error {
 	resp, err := GetAllCore(ctx, addr)
 	if err != nil {
 		return err
 	}
+
 	if len(resp.Pnd) > 0 {
 		pid := resp.Pnd[0].Id
 		viper.Set("CLI_PND", pid)
 		log.Infof("PND: %v", pid)
-		// if len(resp.Pnd[0].Sbi) != 0 {
-		// 	sbi := resp.Pnd[0].Sbi[0].Id
-		// 	viper.Set("CLI_SBI", sbi)
-		// 	log.Infof("SBI: %v", sbi)
-		// }
 	}
+
 	return viper.WriteConfig()
 }
 
 // GetIds requests all UUID information from the controller
-func GetIds(addr string) ([]*ppb.PrincipalNetworkDomain, error) {
-	ctx := context.Background()
+func GetIds(ctx context.Context, addr string) ([]*ppb.PrincipalNetworkDomain, error) {
 	resp, err := GetAllCore(ctx, addr)
 	if err != nil {
 		return nil, err
@@ -74,12 +69,12 @@ func GetAllCore(ctx context.Context, addr string) (*pb.GetPndListResponse, error
 
 // AddPnd takes a name, description and SBI UUID to create a new
 // PrincipalNetworkDomain on the controller
-func AddPnd(addr, name, description, sbi string) (*pb.CreatePndListResponse, error) {
+func AddPnd(ctx context.Context, addr, name, description, sbi string) (*pb.CreatePndListResponse, error) {
 	coreClient, err := nbi.CoreClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	req := &pb.CreatePndListRequest{
 		Timestamp: time.Now().UnixNano(),
 		Pnd: []*pb.PndCreateProperties{
@@ -96,7 +91,7 @@ func AddPnd(addr, name, description, sbi string) (*pb.CreatePndListResponse, err
 
 // GetPnd requests one PrincipalNetworkDomain from the
 // controller.
-func GetPnd(addr string, args ...string) (*pb.GetPndResponse, error) {
+func GetPnd(ctx context.Context, addr string, args ...string) (*pb.GetPndResponse, error) {
 	coreClient, err := nbi.CoreClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -104,7 +99,7 @@ func GetPnd(addr string, args ...string) (*pb.GetPndResponse, error) {
 	if len(args) <= 0 {
 		return nil, errors.New("not enough arguments")
 	}
-	ctx := context.Background()
+
 	req := &pb.GetPndRequest{
 		Timestamp: time.Now().UnixNano(),
 		Pid:       args,
@@ -114,7 +109,7 @@ func GetPnd(addr string, args ...string) (*pb.GetPndResponse, error) {
 
 // GetPnds requests all PrincipalNetworkDomains from the
 // controller.
-func GetPnds(addr string, args ...string) (*pb.GetPndListResponse, error) {
+func GetPnds(ctx context.Context, addr string, args ...string) (*pb.GetPndListResponse, error) {
 	coreClient, err := nbi.CoreClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -122,7 +117,7 @@ func GetPnds(addr string, args ...string) (*pb.GetPndListResponse, error) {
 	if len(args) <= 0 {
 		return nil, errors.New("not enough arguments")
 	}
-	ctx := context.Background()
+
 	req := &pb.GetPndListRequest{
 		Timestamp: time.Now().UnixNano(),
 	}
@@ -130,12 +125,12 @@ func GetPnds(addr string, args ...string) (*pb.GetPndListResponse, error) {
 }
 
 // DeletePnd requests a deletion of the provided PND.
-func DeletePnd(addr string, pid string) (*pb.DeletePndResponse, error) {
+func DeletePnd(ctx context.Context, addr string, pid string) (*pb.DeletePndResponse, error) {
 	coreClient, err := nbi.CoreClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	req := &pb.DeletePndRequest{
 		Timestamp: time.Now().UnixNano(),
 		Pid:       pid,
@@ -145,12 +140,12 @@ func DeletePnd(addr string, pid string) (*pb.DeletePndResponse, error) {
 
 // GetSbi requests one or more to the provided PND belonging SBIs from the
 // controller.
-func GetSbi(addr string, pid string, sid ...string) (*ppb.GetSbiResponse, error) {
+func GetSbi(ctx context.Context, addr string, pid string, sid ...string) (*ppb.GetSbiResponse, error) {
 	client, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	req := &ppb.GetSbiRequest{
 		Timestamp: time.Now().UnixNano(),
 		Pid:       pid,
@@ -160,12 +155,12 @@ func GetSbi(addr string, pid string, sid ...string) (*ppb.GetSbiResponse, error)
 }
 
 //GetSBIs requests all to the provided PND belonging SBIs from the controller.
-func GetSBIs(addr string, pid string) (*ppb.GetSbiListResponse, error) {
+func GetSBIs(ctx context.Context, addr string, pid string) (*ppb.GetSbiListResponse, error) {
 	client, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	req := &ppb.GetSbiListRequest{
 		Timestamp: time.Now().UnixNano(),
 		Pid:       pid,
@@ -174,8 +169,7 @@ func GetSBIs(addr string, pid string) (*ppb.GetSbiListResponse, error) {
 }
 
 // GetChanges requests all pending and unconfirmed changes from the controller
-func GetChanges(addr, pnd string) (*ppb.GetChangeListResponse, error) {
-	ctx := context.Background()
+func GetChanges(ctx context.Context, addr, pnd string) (*ppb.GetChangeListResponse, error) {
 	client, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -189,7 +183,7 @@ func GetChanges(addr, pnd string) (*ppb.GetChangeListResponse, error) {
 
 // Commit sends a Commit request for one or multiple changes to the
 // controller.
-func Commit(addr, pnd string, cuids ...string) (*ppb.SetChangeListResponse, error) {
+func Commit(ctx context.Context, addr, pnd string, cuids ...string) (*ppb.SetChangeListResponse, error) {
 	changes := make([]*ppb.SetChange, len(cuids))
 	for i, arg := range cuids {
 		changes[i] = &ppb.SetChange{
@@ -197,12 +191,12 @@ func Commit(addr, pnd string, cuids ...string) (*ppb.SetChangeListResponse, erro
 			Op:   ppb.Operation_OPERATION_COMMIT,
 		}
 	}
-	return CommitConfirm(addr, pnd, changes)
+	return CommitConfirm(ctx, addr, pnd, changes)
 }
 
 // Confirm sends a Confirm request for one or multiple changes to the
 // controller
-func Confirm(addr, pnd string, cuids ...string) (*ppb.SetChangeListResponse, error) {
+func Confirm(ctx context.Context, addr, pnd string, cuids ...string) (*ppb.SetChangeListResponse, error) {
 	changes := make([]*ppb.SetChange, len(cuids))
 	for i, arg := range cuids {
 		changes[i] = &ppb.SetChange{
@@ -210,12 +204,11 @@ func Confirm(addr, pnd string, cuids ...string) (*ppb.SetChangeListResponse, err
 			Op:   ppb.Operation_OPERATION_CONFIRM,
 		}
 	}
-	return CommitConfirm(addr, pnd, changes)
+	return CommitConfirm(ctx, addr, pnd, changes)
 }
 
 // CommitConfirm confirms a commit
-func CommitConfirm(addr, pnd string, changes []*ppb.SetChange) (*ppb.SetChangeListResponse, error) {
-	ctx := context.Background()
+func CommitConfirm(ctx context.Context, addr, pnd string, changes []*ppb.SetChange) (*ppb.SetChangeListResponse, error) {
 	client, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -230,7 +223,7 @@ func CommitConfirm(addr, pnd string, changes []*ppb.SetChange) (*ppb.SetChangeLi
 
 // AddDevice adds a new device to the controller. The device name is optional.
 // If no name is provided a name will be generated upon device creation.
-func AddDevice(addr, deviceName string, opt *tpb.TransportOption, sid, pid uuid.UUID) (*ppb.SetOndListResponse, error) {
+func AddDevice(ctx context.Context, addr, deviceName string, opt *tpb.TransportOption, sid, pid uuid.UUID) (*ppb.SetOndListResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -257,14 +250,14 @@ func AddDevice(addr, deviceName string, opt *tpb.TransportOption, sid, pid uuid.
 		req.Ond[0].TransportOption.Type = t
 	default:
 	}
-	ctx := context.Background()
+
 	return pndClient.SetOndList(ctx, req)
 }
 
 // GetDevice requests one device belonging to a given
 // PrincipalNetworkDomain from the controller. If no device identifier
 // is provided, an error is thrown.
-func GetDevice(addr, pid string, did ...string) (*ppb.GetOndResponse, error) {
+func GetDevice(ctx context.Context, addr, pid string, did ...string) (*ppb.GetOndResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -279,12 +272,12 @@ func GetDevice(addr, pid string, did ...string) (*ppb.GetOndResponse, error) {
 		Did:       did,
 		Pid:       pid,
 	}
-	ctx := context.Background()
+
 	return pndClient.GetOnd(ctx, req)
 }
 
 // GetSbiSchemaTree gets the sbi tree for a sbi
-func GetSbiSchemaTree(addr string, pid, sid uuid.UUID) (map[string]*yang.Entry, error) {
+func GetSbiSchemaTree(ctx context.Context, addr string, pid, sid uuid.UUID) (map[string]*yang.Entry, error) {
 	sbiClient, err := nbi.SbiClient(addr, dialOptions...)
 	if err != nil {
 		return map[string]*yang.Entry{}, err
@@ -296,7 +289,7 @@ func GetSbiSchemaTree(addr string, pid, sid uuid.UUID) (map[string]*yang.Entry,
 		Sid:       sid.String(),
 	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute*10)
 	defer cancel()
 	sClient, err := sbiClient.GetSchema(ctx, req)
 	if err != nil {
@@ -328,7 +321,7 @@ func GetSbiSchemaTree(addr string, pid, sid uuid.UUID) (map[string]*yang.Entry,
 
 // GetDevices requests all devices belonging to a given
 // PrincipalNetworkDomain from the controller.
-func GetDevices(addr, pid string) (*ppb.GetOndListResponse, error) {
+func GetDevices(ctx context.Context, addr, pid string) (*ppb.GetOndListResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -338,12 +331,12 @@ func GetDevices(addr, pid string) (*ppb.GetOndListResponse, error) {
 		Timestamp: time.Now().UnixNano(),
 		Pid:       pid,
 	}
-	ctx := context.Background()
+
 	return pndClient.GetOndList(ctx, req)
 }
 
 // GetPath requests a specific path
-func GetPath(addr, pid, did, path string) (*ppb.GetPathResponse, error) {
+func GetPath(ctx context.Context, addr, pid, did, path string) (*ppb.GetPathResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -355,12 +348,12 @@ func GetPath(addr, pid, did, path string) (*ppb.GetPathResponse, error) {
 		Pid:       pid,
 		Path:      path,
 	}
-	ctx := context.Background()
+
 	return pndClient.GetPath(ctx, req)
 }
 
 // DeleteDevice deletes a device
-func DeleteDevice(addr, pid, did string) (*ppb.DeleteOndResponse, error) {
+func DeleteDevice(ctx context.Context, addr, pid, did string) (*ppb.DeleteOndResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -371,7 +364,7 @@ func DeleteDevice(addr, pid, did string) (*ppb.DeleteOndResponse, error) {
 		Did:       did,
 		Pid:       pid,
 	}
-	ctx := context.Background()
+
 	return pndClient.DeleteOnd(ctx, req)
 }
 
@@ -379,23 +372,23 @@ func DeleteDevice(addr, pid, did string) (*ppb.DeleteOndResponse, error) {
 // used to specify the type of the change (update, replace, delete as specified
 // in https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-specification.md#34-modifying-state)
 // For delete operations the value field needs to contain an empty string.
-func ChangeRequest(addr, did, pid, path, value string, op ppb.ApiOperation) (*ppb.SetPathListResponse, error) {
+func ChangeRequest(ctx context.Context, addr, did, pid, path, value string, op ppb.ApiOperation) (*ppb.SetPathListResponse, error) {
 	req := &ppb.ChangeRequest{
 		Did:   did,
 		Path:  path,
 		Value: value,
 		ApiOp: op,
 	}
-	return SendChangeRequest(addr, pid, req)
+	return SendChangeRequest(ctx, addr, pid, req)
 }
 
 // SendChangeRequest sends a change request
-func SendChangeRequest(addr, pid string, req *ppb.ChangeRequest) (*ppb.SetPathListResponse, error) {
+func SendChangeRequest(ctx context.Context, addr, pid string, req *ppb.ChangeRequest) (*ppb.SetPathListResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	r := &ppb.SetPathListRequest{
 		Timestamp:     time.Now().UnixNano(),
 		ChangeRequest: []*ppb.ChangeRequest{req},
@@ -405,12 +398,12 @@ func SendChangeRequest(addr, pid string, req *ppb.ChangeRequest) (*ppb.SetPathLi
 }
 
 // Login logs a user in
-func Login(addr, username, pwd string) (*apb.LoginResponse, error) {
+func Login(ctx context.Context, addr, username, pwd string) (*apb.LoginResponse, error) {
 	authClient, err := nbi.AuthClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	r := &apb.LoginRequest{
 		Timestamp: time.Now().UnixNano(),
 		Username:  username,
@@ -420,15 +413,179 @@ func Login(addr, username, pwd string) (*apb.LoginResponse, error) {
 }
 
 // Logout logs a user out
-func Logout(addr, username string) (*apb.LogoutResponse, error) {
+func Logout(ctx context.Context, addr, username string) (*apb.LogoutResponse, error) {
 	authClient, err := nbi.AuthClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
 	}
-	ctx := context.Background()
+
 	r := &apb.LogoutRequest{
 		Timestamp: time.Now().UnixNano(),
 		Username:  username,
 	}
 	return authClient.Logout(ctx, r)
 }
+
+// CreateUsers creates users with provided data
+func CreateUsers(ctx context.Context, addr string, users []*apb.User) (*apb.CreateUsersResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.CreateUsersRequest{
+		Timestamp: time.Now().UnixNano(),
+		User:      users,
+	}
+
+	return authClient.CreateUsers(ctx, r)
+}
+
+//GetUser returns one requested user found by name
+func GetUser(ctx context.Context, addr, name string) (*apb.GetUserResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.GetUserRequest{
+		Timestamp: time.Now().UnixNano(),
+		Name:      name,
+	}
+
+	return authClient.GetUser(ctx, r)
+}
+
+// GetAllUsers return all the available users
+func GetAllUsers(ctx context.Context, addr string) (*apb.GetUsersResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.GetUsersRequest{
+		Timestamp: time.Now().UnixNano(),
+	}
+
+	return authClient.GetUsers(ctx, r)
+}
+
+// UpdateUsers updates all provided users
+func UpdateUsers(ctx context.Context, addr string, users []*apb.User) (*apb.UpdateUsersResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.UpdateUsersRequest{
+		Timestamp: time.Now().UnixNano(),
+		User:      users,
+	}
+
+	return authClient.UpdateUsers(ctx, r)
+}
+
+// DeleteUsers deletes all provided users
+func DeleteUsers(ctx context.Context, addr string, userNames []string) (*apb.DeleteUsersResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.DeleteUsersRequest{
+		Timestamp: time.Now().UnixNano(),
+		Username:  userNames,
+	}
+
+	return authClient.DeleteUsers(ctx, r)
+}
+
+// CreateRoles creates roles with provided data
+func CreateRoles(ctx context.Context, addr string, roles []*apb.Role) (*apb.CreateRolesResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.CreateRolesRequest{
+		Timestamp: time.Now().UnixNano(),
+		Roles:     roles,
+	}
+
+	return authClient.CreateRoles(ctx, r)
+}
+
+// GetRole returns one requested role found by name
+func GetRole(ctx context.Context, addr, name string) (*apb.GetRoleResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.GetRoleRequest{
+		Timestamp: time.Now().UnixNano(),
+		RoleName:  name,
+	}
+
+	return authClient.GetRole(ctx, r)
+}
+
+// GetRoles returns all available roles
+func GetRoles(ctx context.Context, addr string) (*apb.GetRolesResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.GetRolesRequest{
+		Timestamp: time.Now().UnixNano(),
+	}
+
+	return authClient.GetRoles(ctx, r)
+}
+
+// UpdateRoles updates the procided roles
+func UpdateRoles(ctx context.Context, addr string, roles []*apb.Role) (*apb.UpdateRolesResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.UpdateRolesRequest{
+		Timestamp: time.Now().UnixNano(),
+		Roles:     roles,
+	}
+
+	return authClient.UpdateRoles(ctx, r)
+}
+
+// DeletePermissionForRole deletes the provided permissions from one role found by name
+func DeletePermissionForRole(ctx context.Context, addr, name string, permissionsToDelete []string) (*apb.DeletePermissionsForRoleResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.DeletePermissionsForRoleRequest{
+		Timestamp:           time.Now().UnixNano(),
+		RoleName:            name,
+		PermissionsToDelete: permissionsToDelete,
+	}
+
+	return authClient.DeletePermissionsForRole(ctx, r)
+}
+
+// DeleteRoles deletes all the provided roles with their permissions
+func DeleteRoles(ctx context.Context, addr string, roleName []string) (*apb.DeleteRolesResponse, error) {
+	authClient, err := nbi.AuthClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	r := &apb.DeleteRolesRequest{
+		Timestamp: time.Now().UnixNano(),
+		RoleName:  roleName,
+	}
+
+	return authClient.DeleteRoles(ctx, r)
+}
diff --git a/controller/api/initialise_test.go b/controller/api/initialise_test.go
index ab531d3d8232d26653292413ffab973450a70921..efcc249ed10d8106a3fa4e88e2679565a3120282 100644
--- a/controller/api/initialise_test.go
+++ b/controller/api/initialise_test.go
@@ -14,11 +14,13 @@ import (
 	"code.fbi.h-da.de/danet/gosdn/controller/config"
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
 	"code.fbi.h-da.de/danet/gosdn/controller/mocks"
 	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
 	"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
 	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/util/proto"
+	rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac"
 	"code.fbi.h-da.de/danet/yang-models/generated/openconfig"
 	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
@@ -43,6 +45,8 @@ const sbiID = "f6fd4b35-f039-4111-9156-5e4501bb8a5a"
 const ondID = "7e0ed8cc-ebf5-46fa-9794-741494914883"
 
 var pndStore networkdomain.PndStore
+var userService rbac.UserService
+var roleService rbac.RoleService
 var sbiStore southbound.Store
 var lis *bufconn.Listener
 var pndUUID uuid.UUID
@@ -78,6 +82,8 @@ func bootstrapUnitTest() {
 
 	pndStore = nucleus.NewPndStore()
 	sbiStore = nucleus.NewSbiStore(pndUUID)
+	userService = rbacImpl.NewUserService(rbacImpl.NewMemoryUserStore())
+	roleService = rbacImpl.NewRoleService(rbacImpl.NewMemoryRoleStore())
 
 	mockChange := &mocks.Change{}
 	mockChange.On("Age").Return(time.Hour)
@@ -109,7 +115,7 @@ func bootstrapUnitTest() {
 	if err := pndStore.Add(&mockPnd); err != nil {
 		log.Fatal(err)
 	}
-	northbound := nbi.NewNBI(pndStore)
+	northbound := nbi.NewNBI(pndStore, userService, roleService)
 	cpb.RegisterCoreServiceServer(s, northbound.Core)
 	ppb.RegisterPndServiceServer(s, northbound.Pnd)
 	go func() {
diff --git a/controller/controller.go b/controller/controller.go
index 0de0563926466f6ca3ffe93d398c646125f215a9..570c7439a4c164067742cf617fe8206afd32afad 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -24,10 +24,11 @@ import (
 	"code.fbi.h-da.de/danet/gosdn/controller/config"
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
 	"code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
 	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
-	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
+	rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/store"
 
 	"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
@@ -39,6 +40,8 @@ var coreOnce sync.Once
 // Core is the representation of the controller's core
 type Core struct {
 	pndc       networkdomain.PndStore
+	userc      rbac.UserService
+	rolec      rbac.RoleService
 	httpServer *http.Server
 	grpcServer *grpc.Server
 	nbi        *nbi.NorthboundInterface
@@ -58,6 +61,8 @@ func initialize() error {
 
 	c = &Core{
 		pndc:     nucleus.NewPndStore(),
+		userc:    rbacImpl.NewUserService(rbacImpl.NewUserStore()),
+		rolec:    rbacImpl.NewRoleService(rbacImpl.NewRoleStore()),
 		stopChan: make(chan os.Signal, 1),
 	}
 
@@ -93,10 +98,10 @@ func startGrpc() error {
 	}
 	log.Infof("listening to %v", lis.Addr())
 
-	jwtManager := rbac.NewJWTManager("", (60 * time.Minute)) //TODO add real secret and proper duration data here!
+	jwtManager := rbacImpl.NewJWTManager("", (10000 * time.Hour)) //TODO(faseid): add real secret and proper duration data here!
 	setupGRPCServerWithCorrectSecurityLevel(jwtManager)
 
-	c.nbi = nbi.NewNBI(c.pndc)
+	c.nbi = nbi.NewNBI(c.pndc, c.userc, c.rolec)
 	c.nbi.Auth = nbi.NewAuthServer(jwtManager)
 
 	pb.RegisterCoreServiceServer(c.grpcServer, c.nbi.Core)
@@ -204,14 +209,14 @@ func callback(id uuid.UUID, ch chan device.Details) {
 // This allows users to operate on the controller without any authentication/authorization,
 // but they could still login if they want to.
 // Use insecure only for testing purposes and with caution.
-func setupGRPCServerWithCorrectSecurityLevel(jwt *rbac.JWTManager) {
+func setupGRPCServerWithCorrectSecurityLevel(jwt *rbacImpl.JWTManager) {
 	securityLevel := viper.GetString("security")
 	if securityLevel == "insecure" {
 		c.grpcServer = grpc.NewServer()
 		log.Info("set up grpc server in insecure mode")
 	} else {
 		interceptor := server.NewAuthInterceptor(jwt)
-		c.grpcServer = grpc.NewServer(grpc.UnaryInterceptor(interceptor.Unary()))
+		c.grpcServer = grpc.NewServer(grpc.UnaryInterceptor(interceptor.Unary()), grpc.StreamInterceptor(interceptor.Stream()))
 		log.Info("set up grpc server in secure mode")
 	}
 }
diff --git a/controller/interfaces/rbac/rbacService.go b/controller/interfaces/rbac/rbacService.go
new file mode 100644
index 0000000000000000000000000000000000000000..e5a543e8ce69a1e5237906ec225e675271ad6d72
--- /dev/null
+++ b/controller/interfaces/rbac/rbacService.go
@@ -0,0 +1,40 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+)
+
+// UserService describes an interface for user service implementation.
+type UserService interface {
+	Add(User) error
+	Delete(User) error
+	Update(User) error
+	Get(store.Query) (User, error)
+	GetAll() ([]User, error)
+}
+
+// LoadedUser represents a User that was loaded
+type LoadedUser struct {
+	ID       string            `json:"_id" bson:"_id"`
+	UserName string            `json:"username"`
+	Roles    map[string]string `json:"roles,omitempty"`
+	Password string            `json:"password"`
+	Token    string            `json:"token,omitempty"`
+}
+
+// RoleService describes an interface for role service implementations.
+type RoleService interface {
+	Add(Role) error
+	Delete(Role) error
+	Update(Role) error
+	Get(store.Query) (Role, error)
+	GetAll() ([]Role, error)
+}
+
+// LoadedRole represents a Role that was loaded
+type LoadedRole struct {
+	ID          string   `json:"_id" bson:"_id"`
+	RoleName    string   `json:"rolename"`
+	Description string   `json:"description,omitempty"`
+	Permissions []string `json:"permissions,omitempty"`
+}
diff --git a/controller/interfaces/rbac/role.go b/controller/interfaces/rbac/role.go
new file mode 100644
index 0000000000000000000000000000000000000000..41ab642f9ba4f0e781307e6f0888503a1280dd22
--- /dev/null
+++ b/controller/interfaces/rbac/role.go
@@ -0,0 +1,12 @@
+package rbac
+
+import "github.com/google/uuid"
+
+// Role represents a role with permissions
+type Role interface {
+	ID() uuid.UUID
+	Name() string
+	GetDescription() string
+	GetPermissions() []string
+	RemovePermissionsFromRole([]string)
+}
diff --git a/controller/interfaces/rbac/roleStore.go b/controller/interfaces/rbac/roleStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..88ee8f59feb93ac99aaf61c35c2ab0b9f94af9ce
--- /dev/null
+++ b/controller/interfaces/rbac/roleStore.go
@@ -0,0 +1,12 @@
+package rbac
+
+import "code.fbi.h-da.de/danet/gosdn/controller/store"
+
+// RoleStore describes an interface for role store implementations.
+type RoleStore interface {
+	Add(r Role) error
+	Update(r Role) error
+	Delete(Role) error
+	Get(store.Query) (LoadedRole, error)
+	GetAll() ([]LoadedRole, error)
+}
diff --git a/controller/interfaces/rbac/user.go b/controller/interfaces/rbac/user.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ed0fc2549dd0f40bea845c81f4bbe2b944beccf
--- /dev/null
+++ b/controller/interfaces/rbac/user.go
@@ -0,0 +1,13 @@
+package rbac
+
+import "github.com/google/uuid"
+
+// User represents an User which is managed by rbac
+type User interface {
+	ID() uuid.UUID
+	Name() string
+	GetRoles() map[string]string
+	GetPassword() string
+	GetToken() string
+	SetToken(string)
+}
diff --git a/controller/interfaces/rbac/userStore.go b/controller/interfaces/rbac/userStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..014a20ce0ae06cc0091f9fefa4ab5b834059fafd
--- /dev/null
+++ b/controller/interfaces/rbac/userStore.go
@@ -0,0 +1,14 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+)
+
+// UserStore describes an interface for user store implementations.
+type UserStore interface {
+	Add(u User) error
+	Update(u User) error
+	Delete(User) error
+	Get(store.Query) (LoadedUser, error)
+	GetAll() ([]LoadedUser, error)
+}
diff --git a/controller/northbound/server/auth.go b/controller/northbound/server/auth.go
index 2307abf276d0270c2e7ad4c716de81533382878f..4c159193a9e65bd2e56698ba2c11da7a7cb47202 100644
--- a/controller/northbound/server/auth.go
+++ b/controller/northbound/server/auth.go
@@ -7,7 +7,10 @@ import (
 	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/metrics"
 	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+	"github.com/google/uuid"
 	"github.com/prometheus/client_golang/prometheus"
+	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 )
@@ -27,29 +30,21 @@ func NewAuthServer(jwtManager *rbac.JWTManager) *Auth {
 
 // Login logs a user in
 func (s Auth) Login(ctx context.Context, request *apb.LoginRequest) (*apb.LoginResponse, error) {
-	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 
 	user := rbac.User{
-		Name:     request.Username,
+		UserName: request.Username,
 		Password: request.Pwd,
 	}
 
-	// check if user is already logged in
-	loggedIn, err := s.isLoggedIn(user.Name)
-	if err != nil {
-		return nil, err
-	} else if loggedIn {
-		return nil, status.Errorf(codes.Canceled, "already logged in")
-	}
+	//TODO: add check if user is logged in with session handling
 
 	// validation of credentials
 	validCredentials, err := s.isValidUser(user)
-	if err != nil {
+	if (err != nil) || !validCredentials {
 		return nil, err
-	} else if !validCredentials {
-		return nil, status.Errorf(codes.Unauthenticated, "incorrect user name or password")
 	}
 
 	// generate token, persist session and return to user
@@ -58,7 +53,17 @@ func (s Auth) Login(ctx context.Context, request *apb.LoginRequest) (*apb.LoginR
 		return nil, err
 	}
 
-	//TODO(faseid): persist token for session handling here!
+	userToUpdate, err := userc.Get(store.Query{Name: user.UserName})
+	if err != nil {
+		return nil, err
+	}
+
+	userToUpdate.SetToken(token)
+
+	err = userc.Update(userToUpdate)
+	if err != nil {
+		return nil, err
+	}
 
 	return &apb.LoginResponse{
 		Timestamp: time.Now().UnixNano(),
@@ -69,18 +74,12 @@ func (s Auth) Login(ctx context.Context, request *apb.LoginRequest) (*apb.LoginR
 
 // Logout logs a user out
 func (s Auth) Logout(ctx context.Context, request *apb.LogoutRequest) (*apb.LogoutResponse, error) {
-	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 
-	loggedIn, err := s.isLoggedIn(request.Username)
-	if err != nil {
-		return nil, err
-	} else if !loggedIn {
-		return nil, status.Errorf(codes.Canceled, "not logged in")
-	} else if loggedIn {
-		// TODO(faseid): delete active session from storage
-	}
+	// not implemented yet
+	// TODO: add session handling and logout
 
 	return &apb.LogoutResponse{
 		Timestamp: time.Now().UnixNano(),
@@ -90,11 +89,29 @@ func (s Auth) Logout(ctx context.Context, request *apb.LogoutRequest) (*apb.Logo
 
 // CreateUsers creates new users, can be 1 or more
 func (s Auth) CreateUsers(ctx context.Context, request *apb.CreateUsersRequest) (*apb.CreateUsersResponse, error) {
-	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 
-	// TODO: implement proper user creation
+	// TODO: implement check if user is allowed to create users with this role
+	// e.g. non-admin shouldn't be allowed to create admin users
+	for _, u := range request.User {
+		roles := map[string]string{}
+		for key, elem := range u.Roles {
+			_, err := uuid.Parse(key)
+			if err != nil {
+				return nil, handleRPCError(labels, err)
+			}
+			roles[key] = elem
+		}
+
+		user := rbac.NewUser(uuid.New(), u.Name, roles, u.Password, u.Token)
+		err := userc.Add(user)
+		if err != nil {
+			log.Error(err)
+			return nil, status.Errorf(codes.Aborted, "%v", err)
+		}
+	}
 
 	return &apb.CreateUsersResponse{
 		Timestamp: time.Now().UnixNano(),
@@ -102,28 +119,83 @@ func (s Auth) CreateUsers(ctx context.Context, request *apb.CreateUsersRequest)
 	}, nil
 }
 
+// GetUser returns one user by name.
+func (s Auth) GetUser(ctx context.Context, request *apb.GetUserRequest) (*apb.GetUserResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "get"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement check if user is allowed to get this user data; only their own if not admin
+	userData, err := userc.Get(store.Query{Name: request.Name})
+	if err != nil {
+		return nil, err
+	}
+
+	user := &apb.User{
+		Id:    userData.ID().String(),
+		Name:  userData.Name(),
+		Roles: userData.GetRoles(),
+	}
+
+	return &apb.GetUserResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+		User:      user,
+	}, nil
+}
+
 // GetUsers returns all availbale users
 func (s Auth) GetUsers(ctx context.Context, request *apb.GetUsersRequest) (*apb.GetUsersResponse, error) {
-	labels := prometheus.Labels{"service": "core", "rpc": "get"}
+	labels := prometheus.Labels{"service": "auth", "rpc": "get"}
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 
-	// TODO: implement proper user fetching
+	userList, err := userc.GetAll()
+	if err != nil {
+		return nil, err
+	}
+
+	users := []*apb.User{}
+	for _, u := range userList {
+		users = append(users, &apb.User{
+			Id:    u.ID().String(),
+			Name:  u.Name(),
+			Roles: u.GetRoles(),
+		})
+	}
 
 	return &apb.GetUsersResponse{
 		Timestamp: time.Now().UnixNano(),
 		Status:    apb.Status_STATUS_OK,
-		// User here, probably needs fixing in proto!
+		User:      users,
 	}, nil
 }
 
 // UpdateUsers updates the user data of one or more users provided in the request
 func (s Auth) UpdateUsers(ctx context.Context, request *apb.UpdateUsersRequest) (*apb.UpdateUsersResponse, error) {
-	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 
-	// TODO: implement proper user updating here
+	// TODO: check if current user is allowed to update the user they try to update; only their own if not admin
+	for _, u := range request.User {
+		uid, err := uuid.Parse(u.Id)
+		if err != nil {
+			return nil, handleRPCError(labels, err)
+		}
+
+		_, err = userc.Get(store.Query{ID: uid})
+		if err != nil {
+			return nil, status.Errorf(codes.Canceled, "user not found %v", err)
+		}
+
+		userToUpdate := rbac.NewUser(uid, u.Name, u.Roles, u.Password, u.Token)
+
+		err = userc.Update(userToUpdate)
+		if err != nil {
+			return nil, status.Errorf(codes.Aborted, "could not update user %v", err)
+		}
+	}
 
 	return &apb.UpdateUsersResponse{
 		Timestamp: time.Now().UnixNano(),
@@ -133,33 +205,212 @@ func (s Auth) UpdateUsers(ctx context.Context, request *apb.UpdateUsersRequest)
 
 // DeleteUsers deletes one or more users provided in the request
 func (s Auth) DeleteUsers(ctx context.Context, request *apb.DeleteUsersRequest) (*apb.DeleteUsersResponse, error) {
-	labels := prometheus.Labels{"service": "core", "rpc": "delete"}
+	labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 
-	// TODO: implement proper user deletion
+	for _, u := range request.Username {
+		userToDelete, err := userc.Get(store.Query{Name: u})
+		if err != nil {
+			return nil, status.Errorf(codes.Canceled, "user not found %v", err)
+		}
 
+		err = userc.Delete(userToDelete)
+		if err != nil {
+			return nil, status.Errorf(codes.Aborted, "error deleting user %v", err)
+		}
+	}
 	return &apb.DeleteUsersResponse{
 		Timestamp: time.Now().UnixNano(),
 		Status:    apb.Status_STATUS_OK,
 	}, nil
 }
 
-//TODO(faseid): implement proper log in check
-func (s Auth) isLoggedIn(username string) (bool, error) {
-	// if user not found
-	// return nil, err
+func (s Auth) isValidUser(user rbac.User) (bool, error) {
+	storedUser, err := userc.Get(store.Query{Name: user.Name()})
+	if err != nil {
+		return false, err
+	} else if storedUser == nil {
+		return false, status.Errorf(codes.Aborted, "no user object")
+	}
 
-	// if already user logged in
-	// return true, nil
+	if storedUser.Name() == user.Name() {
+		if storedUser.GetPassword() == user.GetPassword() {
+			return true, nil
+		}
+	}
 
-	return false, nil
+	return false, status.Errorf(codes.Unauthenticated, "incorrect user name or password")
 }
 
-// TODO(faseid): implement proper validation
-func (s Auth) isValidUser(user rbac.User) (bool, error) {
-	// check correct credentials here
+// CreateRoles creates roles with permissions for the roles used in rbac.
+func (s Auth) CreateRoles(ctx context.Context, request *apb.CreateRolesRequest) (*apb.CreateRolesResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	for _, r := range request.Roles {
+		role := rbac.NewRole(uuid.New(), r.Name, r.Description, r.Permissions)
+
+		err := rolec.Add(role)
+		if err != nil {
+			log.Error(err)
+			return nil, status.Errorf(codes.Aborted, "%v", err)
+		}
+	}
+
+	return &apb.CreateRolesResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
+
+// GetRole returns one role with its permissions found by name.
+func (s Auth) GetRole(ctx context.Context, request *apb.GetRoleRequest) (*apb.GetRoleResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "get"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	roleData, err := rolec.Get(store.Query{Name: request.RoleName})
+	if err != nil {
+		return nil, err
+	}
+
+	role := &apb.Role{
+		Id:          roleData.ID().String(),
+		Name:        roleData.Name(),
+		Description: roleData.GetDescription(),
+		Permissions: roleData.GetPermissions(),
+	}
+
+	return &apb.GetRoleResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+		Role:      role,
+	}, nil
+}
+
+// GetRoles returns all roles with their permissions.
+func (s Auth) GetRoles(ctx context.Context, request *apb.GetRolesRequest) (*apb.GetRolesResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "get"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	roleList, err := rolec.GetAll()
+	if err != nil {
+		return nil, err
+	}
 
-	// return true for now, change to false when there is a user storage for actual validation available
-	return true, nil
+	roles := []*apb.Role{}
+	for _, r := range roleList {
+		roles = append(roles, &apb.Role{
+			Id:          r.ID().String(),
+			Name:        r.Name(),
+			Description: r.GetDescription(),
+			Permissions: r.GetPermissions(),
+		})
+	}
+
+	return &apb.GetRolesResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+		Roles:     roles,
+	}, nil
+}
+
+// UpdateRoles updates data of the provided roles.
+func (s Auth) UpdateRoles(ctx context.Context, request *apb.UpdateRolesRequest) (*apb.UpdateRolesResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: check if current user is allowed to update the role they try to update; only their own if not admin
+	for _, r := range request.Roles {
+		rid, err := uuid.Parse(r.Id)
+		if err != nil {
+			return nil, handleRPCError(labels, err)
+		}
+
+		_, err = rolec.Get(store.Query{ID: rid})
+		if err != nil {
+			return nil, status.Errorf(codes.Canceled, "role not found %v", err)
+		}
+
+		roleToUpdate := rbac.NewRole(rid, r.Name, r.Description, r.Permissions)
+		err = rolec.Update(roleToUpdate)
+		if err != nil {
+			return nil, status.Errorf(codes.Aborted, "could not update role %v", err)
+		}
+	}
+
+	return &apb.UpdateRolesResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
+
+// DeletePermissionsForRole deletes the provided permissions from one role found by name.
+func (s Auth) DeletePermissionsForRole(ctx context.Context, request *apb.DeletePermissionsForRoleRequest) (*apb.DeletePermissionsForRoleResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	roleToUpdate, err := rolec.Get(store.Query{Name: request.RoleName})
+	if err != nil {
+		return nil, status.Errorf(codes.Canceled, "role not found %v", err)
+	}
+
+	// checks if there is at least one valid permission to delete
+	// in the provided set of permissions to delete
+	nonFound := true
+	for _, perm := range roleToUpdate.GetPermissions() {
+		for _, permToDelete := range request.PermissionsToDelete {
+			if perm == permToDelete {
+				nonFound = false
+				break
+			}
+		}
+		if !nonFound {
+			break
+		}
+	}
+	if nonFound {
+		return nil, status.Errorf(codes.Canceled, "no fitting permissions")
+	}
+
+	// updates the existing role with the trimmed set of permissions
+	roleToUpdate.RemovePermissionsFromRole(request.PermissionsToDelete)
+	err = rolec.Update(roleToUpdate)
+	if err != nil {
+		return nil, status.Errorf(codes.Aborted, "could not update role %v", err)
+	}
+
+	return &apb.DeletePermissionsForRoleResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
+
+// DeleteRoles deletes all the provided roles with their permissions.
+func (s Auth) DeleteRoles(ctx context.Context, request *apb.DeleteRolesRequest) (*apb.DeleteRolesResponse, error) {
+	labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	for _, r := range request.RoleName {
+		roleToDelete, err := rolec.Get(store.Query{Name: r})
+		if err != nil {
+			return nil, status.Errorf(codes.Canceled, "role not found")
+		}
+
+		err = rolec.Delete(roleToDelete)
+		if err != nil {
+			return nil, status.Errorf(codes.Aborted, "error deleting role %v", err)
+		}
+	}
+
+	return &apb.DeleteRolesResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
 }
diff --git a/controller/northbound/server/auth_interceptor.go b/controller/northbound/server/auth_interceptor.go
index 65d63fd7765beaf620307d3f7497ac6a02d8c3f2..428b5873b80ddcc9ee24d793bab40f36ed4b6020 100644
--- a/controller/northbound/server/auth_interceptor.go
+++ b/controller/northbound/server/auth_interceptor.go
@@ -5,8 +5,11 @@ import (
 
 	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
-	log "github.com/sirupsen/logrus"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
 )
 
 // AuthInterceptor provides an AuthInterceptor
@@ -21,23 +24,122 @@ func NewAuthInterceptor(jwtManager *rbac.JWTManager) *AuthInterceptor {
 	}
 }
 
-// Unary provides middleware functionality
-func (auth AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
+// Unary returns a unary interceptor function to authenticate and authorize unary RPC calls
+func (auth *AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
 	return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
-		// TODO: Implement proper auth logic here
-		if _, ok := req.(*apb.LoginRequest); ok {
+		switch r := req.(type) {
+		case *apb.LoginRequest:
 			return handler(ctx, req)
-		}
+		case *apb.LogoutRequest:
+			return handler(ctx, req)
+		case *apb.CreateUsersRequest:
+			if len(r.User) < 2 {
+				return handler(ctx, req)
+			}
 
-		// // validate token here
-		// claims, err := auth.jwtManager.VerifyToken("") // add token from context here!
-		// if err != nil {
-		// 	return nil, status.Errorf(codes.PermissionDenied, "%v", err)
-		// }
-		// // use claims for authorization
-		// log.Info("User: " + claims.Username)
-		log.Info("Interceptor called")
+			err := auth.authorize(ctx, info.FullMethod)
+			if err != nil {
+				return nil, err
+			}
+		default:
+			err := auth.authorize(ctx, info.FullMethod)
+			if err != nil {
+				return nil, err
+			}
+		}
 
 		return handler(ctx, req)
 	}
 }
+
+// Stream returns a server interceptor function to authorize stream RPC calls
+func (auth *AuthInterceptor) Stream() grpc.StreamServerInterceptor {
+	return func(
+		srv interface{},
+		stream grpc.ServerStream,
+		info *grpc.StreamServerInfo,
+		handler grpc.StreamHandler,
+	) error {
+		err := auth.authorize(stream.Context(), info.FullMethod)
+		if err != nil {
+			return err
+		}
+
+		return handler(srv, stream)
+	}
+}
+
+func (auth *AuthInterceptor) authorize(ctx context.Context, method string) error {
+	md, ok := metadata.FromIncomingContext(ctx)
+	if !ok {
+		return status.Errorf(codes.Unauthenticated, "metadata is not provided")
+	}
+
+	// validate token and check permission here
+	token := ""
+	if len(md["authorize"]) > 0 {
+		token = md["authorize"][0]
+
+		claims, err := auth.jwtManager.VerifyToken(token)
+		if err != nil {
+			return err
+		}
+
+		user, err := userc.Get(store.Query{Name: claims.Username})
+		if err != nil {
+			return err
+		}
+
+		if user.GetToken() != token {
+			return status.Errorf(codes.PermissionDenied, "invalid token")
+		}
+
+		err = auth.verifyPermisisonForRequestedCall(user.GetRoles(), method)
+		if err != nil {
+			return err
+		}
+	} else {
+		return status.Errorf(codes.PermissionDenied, "no auth token provided")
+	}
+
+	return nil
+}
+
+func (auth *AuthInterceptor) verifyPermisisonForRequestedCall(userRoles map[string]string, requestedMethod string) error {
+	for _, userRole := range userRoles {
+		err := auth.verifyUserRoleAndRequestedCall(userRole, requestedMethod)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (auth *AuthInterceptor) verifyUserRoleAndRequestedCall(userRole, requestedMethod string) error {
+	storedRoles, err := rolec.GetAll()
+	if err != nil {
+		return err
+	}
+
+	for _, storedRole := range storedRoles {
+		if userRole == storedRole.Name() {
+			err := auth.compareRequestedPermissionWithRolePermissions(requestedMethod, storedRole.GetPermissions())
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func (auth *AuthInterceptor) compareRequestedPermissionWithRolePermissions(requestedMethod string, storedRolePermissions []string) error {
+	for _, permission := range storedRolePermissions {
+		if permission == requestedMethod {
+			return nil
+		}
+	}
+
+	return status.Errorf(codes.PermissionDenied, "user not authorized for this call")
+}
diff --git a/controller/northbound/server/auth_test.go b/controller/northbound/server/auth_test.go
index d8f8746d9a2b0e33263980fb0b742c6c7689fd2c..ba6487644fe11d68577783a293b11016ae4a325b 100644
--- a/controller/northbound/server/auth_test.go
+++ b/controller/northbound/server/auth_test.go
@@ -1,135 +1,219 @@
 package server
 
 import (
+	"bytes"
 	"context"
+	"log"
 	"reflect"
 	"testing"
-	"time"
 
 	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
+	"github.com/google/uuid"
 )
 
-func Test_auth_Login(t *testing.T) {
-	type fields struct {
-		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
-	}
+const adminID = "5c248a22-8eb7-48cf-b392-45680a1863a5"
+const userID = "57005d13-7a4d-493d-a02b-50ca51c40197"
+const adminRoleID = "126683ae-5ff2-43ee-92f7-0e2b936f8c77"
+const randomRoleName = "bertram"
+
+var adminRoleMap = map[string]string{pndID: "admin"}
+var userRoleMap = map[string]string{pndID: "user"}
+var jwt *rbac.JWTManager
+
+func TestAuth_Login(t *testing.T) {
 	type args struct {
 		ctx     context.Context
 		request *apb.LoginRequest
 	}
 	tests := []struct {
 		name    string
-		fields  fields
 		args    args
 		want    string
 		wantErr bool
 	}{
-		// TODO: Add test cases.
 		{
-			name: "login test",
+			name: "default login",
+			want: "testAdmin",
+			args: args{
+				request: &apb.LoginRequest{
+					Username: "testAdmin",
+					Pwd:      "admin",
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "login fail wrong pwd",
 			want: "",
 			args: args{
-				request: &apb.LoginRequest{},
+				request: &apb.LoginRequest{
+					Username: "testAdmin",
+					Pwd:      "nope",
+				},
 			},
+			wantErr: true,
 		},
 	}
 
-	jwt := rbac.NewJWTManager("", 1*time.Minute)
-
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			r := Auth{
-				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
-				jwtManager:                     jwt,
+				jwtManager: jwt,
 			}
 			resp, err := r.Login(tt.args.ctx, tt.args.request)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("rbac.Login() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Auth.Login() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 
-			got := resp.GetStatus().String()
-			got = ""
-
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("rbac.Login() = %v, want %v", got, tt.want)
+			if resp != nil {
+				got := resp.Token
+				if got == "" {
+					t.Errorf("Auth.Login() = %v, want non empty token", got)
+				}
 			}
 		})
 	}
 }
 
-func Test_auth_Logout(t *testing.T) {
-	type fields struct {
-		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
-	}
+func TestAuth_Logout(t *testing.T) {
 	type args struct {
 		ctx     context.Context
 		request *apb.LogoutRequest
 	}
 	tests := []struct {
 		name    string
-		fields  fields
 		args    args
 		want    *apb.LogoutResponse
 		wantErr bool
 	}{
 		// TODO: Add test cases.
+		// Implement after session hdanling was added
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			r := Auth{
-				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
-			}
-			got, err := r.Logout(tt.args.ctx, tt.args.request)
+			s := Auth{}
+			got, err := s.Logout(tt.args.ctx, tt.args.request)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("rbac.Logout() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Auth.Logout() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("rbac.Logout() = %v, want %v", got, tt.want)
+				t.Errorf("Auth.Logout() = %v, want %v", got, tt.want)
 			}
 		})
 	}
 }
 
-func Test_rbac_CreateUsers(t *testing.T) {
-	type fields struct {
-		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
-	}
+func TestAuth_CreateUsers(t *testing.T) {
 	type args struct {
 		ctx     context.Context
 		request *apb.CreateUsersRequest
 	}
 	tests := []struct {
 		name    string
-		fields  fields
 		args    args
-		want    *apb.CreateUsersResponse
+		want    apb.Status
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "default create users",
+			args: args{ctx: context.TODO(),
+				request: &apb.CreateUsersRequest{
+					User: []*apb.User{
+						{
+							Name:     "asdf",
+							Roles:    map[string]string{pndID: "asdf"},
+							Password: "asdf",
+							Token:    "",
+						},
+					},
+				},
+			},
+			want:    apb.Status_STATUS_OK,
+			wantErr: false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			r := Auth{
-				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			s := Auth{}
+			got, err := s.CreateUsers(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.CreateUsers() error = %v, wantErr %v", err, tt.wantErr)
+				return
 			}
-			got, err := r.CreateUsers(tt.args.ctx, tt.args.request)
+
+			if !reflect.DeepEqual(got.Status, tt.want) {
+				t.Errorf("Auth.CreateUsers() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestAuth_GetUser(t *testing.T) {
+	patchLogger(t)
+	type args struct {
+		ctx     context.Context
+		request *apb.GetUserRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.GetUserResponse
+		wantErr bool
+	}{
+		{
+			name: "default get user",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.GetUserRequest{
+					Name: "testAdmin",
+				},
+			},
+			want: &apb.GetUserResponse{Status: apb.Status_STATUS_OK,
+				User: &apb.User{Id: adminID,
+					Name: "testAdmin"}},
+			wantErr: false,
+		},
+		{
+			name: "fail get user",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.GetUserRequest{
+					Name: "nope",
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.GetUser(tt.args.ctx, tt.args.request)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("rbac.CreateUsers() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Auth.GetUser() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("rbac.CreateUsers() = %v, want %v", got, tt.want)
+
+			if got != nil && got.Status == tt.want.Status {
+				if got.User.Name != tt.want.User.Name || got.User.Id != tt.want.User.Id {
+					t.Errorf("Auth.GetUser() = %v, want %v", got, tt.want)
+				}
+			} else {
+				if got != nil {
+					t.Errorf("Auth.GetUser() = %v, want %v", got, tt.want)
+				}
 			}
 		})
 	}
 }
 
-func Test_rbac_GetUsers(t *testing.T) {
-	type fields struct {
-		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+func TestAuth_GetUsers(t *testing.T) {
+	err := clearAndCreateAuthTestSetup()
+	if err != nil {
+		t.Fatalf("%v", err)
 	}
 	type args struct {
 		ctx     context.Context
@@ -137,94 +221,631 @@ func Test_rbac_GetUsers(t *testing.T) {
 	}
 	tests := []struct {
 		name    string
-		fields  fields
 		args    args
 		want    *apb.GetUsersResponse
+		wantLen int
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "default get users",
+			args: args{ctx: context.TODO(),
+				request: &apb.GetUsersRequest{},
+			},
+			want: &apb.GetUsersResponse{Status: apb.Status_STATUS_OK,
+				User: []*apb.User{
+					{Name: "testAdmin"},
+					{Name: "testUser"},
+					{Name: "testRandom"}},
+			},
+			wantLen: 3,
+			wantErr: false,
+		},
 	}
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			r := Auth{
-				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
-			}
-			got, err := r.GetUsers(tt.args.ctx, tt.args.request)
+			s := Auth{}
+			got, err := s.GetUsers(tt.args.ctx, tt.args.request)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("rbac.GetUsers() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Auth.GetUsers() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("rbac.GetUsers() = %v, want %v", got, tt.want)
+
+			if got != nil && got.Status == apb.Status_STATUS_OK {
+				if len(got.User) != tt.wantLen {
+					t.Errorf("Auth.GetUsers() = %v, want %v", got, tt.want)
+				}
+
+				for _, gotU := range got.User {
+					containsExpected := false
+					for _, wantU := range tt.want.User {
+						if gotU.Name == wantU.Name {
+							containsExpected = true
+							break
+						}
+					}
+					if !containsExpected {
+						t.Errorf("Auth.GetUsers() = %v, want %v", got, tt.want)
+					}
+				}
 			}
 		})
 	}
 }
 
-func Test_rbac_UpdateUsers(t *testing.T) {
-	type fields struct {
-		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
-	}
+func TestAuth_UpdateUsers(t *testing.T) {
 	type args struct {
 		ctx     context.Context
 		request *apb.UpdateUsersRequest
 	}
 	tests := []struct {
 		name    string
-		fields  fields
 		args    args
 		want    *apb.UpdateUsersResponse
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "default update user",
+			args: args{ctx: context.TODO(),
+				request: &apb.UpdateUsersRequest{User: []*apb.User{
+					{Id: adminID,
+						Name: "sth Else"},
+				},
+				},
+			},
+			want: &apb.UpdateUsersResponse{
+				Status: apb.Status_STATUS_OK},
+			wantErr: false,
+		},
+		{
+			name: "error update user",
+			args: args{ctx: context.TODO(),
+				request: &apb.UpdateUsersRequest{User: []*apb.User{
+					{Id: uuid.NewString(),
+						Name: "not a user"},
+				},
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
 	}
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			r := Auth{
-				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
-			}
-			got, err := r.UpdateUsers(tt.args.ctx, tt.args.request)
+			s := Auth{}
+			got, err := s.UpdateUsers(tt.args.ctx, tt.args.request)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("rbac.UpdateUsers() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Auth.UpdateUsers() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("rbac.UpdateUsers() = %v, want %v", got, tt.want)
+			if got != nil && got.Status != tt.want.Status {
+				t.Errorf("Auth.UpdateUsers() = %v, want %v", got, tt.want)
 			}
 		})
 	}
 }
 
-func Test_rbac_DeleteUsers(t *testing.T) {
-	type fields struct {
-		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
-	}
+func TestAuth_DeleteUsers(t *testing.T) {
 	type args struct {
 		ctx     context.Context
 		request *apb.DeleteUsersRequest
 	}
 	tests := []struct {
 		name    string
-		fields  fields
 		args    args
 		want    *apb.DeleteUsersResponse
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "default delete users",
+			args: args{ctx: context.TODO(),
+				request: &apb.DeleteUsersRequest{Username: []string{"testUser"}},
+			},
+			want:    &apb.DeleteUsersResponse{Status: apb.Status_STATUS_OK},
+			wantErr: false,
+		},
+		{
+			name: "error delete users",
+			args: args{ctx: context.TODO(),
+				request: &apb.DeleteUsersRequest{Username: []string{"no user"}},
+			},
+			want:    &apb.DeleteUsersResponse{Status: apb.Status_STATUS_OK},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			r := Auth{
-				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			s := Auth{}
+			got, err := s.DeleteUsers(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.DeleteUsers() error = %v, wantErr %v", err, tt.wantErr)
+				return
 			}
-			got, err := r.DeleteUsers(tt.args.ctx, tt.args.request)
+			if got != nil && got.Status != tt.want.Status {
+				t.Errorf("Auth.DeleteUsers() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestAuth_CreateRoles(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		request *apb.CreateRolesRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.CreateRolesResponse
+		wantErr bool
+	}{
+		{
+			name: "default create roles",
+			args: args{ctx: context.TODO(),
+				request: &apb.CreateRolesRequest{
+					Roles: []*apb.Role{
+						{
+							Name:        "new role 1",
+							Description: "Role 1",
+							Permissions: []string{"permission 1", "permission 2"},
+						},
+					},
+				},
+			},
+			want:    &apb.CreateRolesResponse{Status: apb.Status_STATUS_OK},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.CreateRoles(tt.args.ctx, tt.args.request)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("rbac.DeleteUsers() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("Auth.CreateRoles() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("rbac.DeleteUsers() = %v, want %v", got, tt.want)
+
+			if got != nil && got.Status != tt.want.Status {
+				t.Errorf("Auth.CreateRoles() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestAuth_GetRole(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		request *apb.GetRoleRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.GetRoleResponse
+		wantErr bool
+	}{
+		{
+			name: "default get role",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.GetRoleRequest{
+					RoleName: "adminTestRole",
+				},
+			},
+			want: &apb.GetRoleResponse{
+				Role: &apb.Role{
+					Name:        "adminTestRole",
+					Description: "Admin",
+				},
+				Status: apb.Status_STATUS_OK,
+			},
+			wantErr: false,
+		},
+		{
+			name: "error get role",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.GetRoleRequest{
+					RoleName: "not role",
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.GetRole(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.GetRole() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != nil && got.Status == tt.want.Status {
+				if got.Role.Name != tt.want.Role.Name || got.Role.Description != tt.want.Role.Description {
+					t.Errorf("Auth.GetRole() = %v, want %v", got, tt.want)
+				}
+			} else {
+				if got != nil {
+					t.Errorf("Auth.GetRole() = %v, want %v", got, tt.want)
+				}
+			}
+		})
+	}
+}
+
+func TestAuth_GetRoles(t *testing.T) {
+	err := clearAndCreateAuthTestSetup()
+	if err != nil {
+		t.Fatalf("%v", err)
+	}
+
+	type args struct {
+		ctx     context.Context
+		request *apb.GetRolesRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.GetRolesResponse
+		wantLen int
+		wantErr bool
+	}{
+		{
+			name: "default get roles",
+			args: args{
+				ctx:     context.TODO(),
+				request: &apb.GetRolesRequest{},
+			},
+			want: &apb.GetRolesResponse{
+				Status: apb.Status_STATUS_OK,
+				Roles: []*apb.Role{
+					{
+						Name:        "adminTestRole",
+						Description: "Admin",
+						Permissions: []string{
+							"/gosdn.core.CoreService/GetPnd",
+							"/gosdn.core.CoreService/GetPndList",
+						}},
+					{
+						Name:        "userTestRole",
+						Description: "User",
+						Permissions: []string{
+							"/gosdn.pnd.PndService/GetChangeList",
+						}},
+					{
+						Name:        randomRoleName,
+						Description: "Not a role",
+						Permissions: []string{
+							"nope",
+						},
+					},
+				},
+			},
+			wantLen: 3,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.GetRoles(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.GetRoles() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != nil && got.Status == tt.want.Status {
+				if len(got.Roles) != 3 {
+					t.Errorf("Auth.GetRoles() = %v, want %v", got, tt.want)
+				}
+				for _, gotR := range got.Roles {
+					containsExpected := false
+					for _, wantR := range tt.want.Roles {
+						gotPerm := gotR.Permissions
+						wantPerm := wantR.Permissions
+						if gotR.Description == wantR.Description && gotR.Name == wantR.Name &&
+							reflect.DeepEqual(gotPerm, wantPerm) {
+							containsExpected = true
+							break
+						}
+					}
+					if !containsExpected {
+						t.Errorf("Auth.GetRoles() = %v, want %v", got, tt.want)
+					}
+				}
 			}
 		})
 	}
 }
+
+func TestAuth_UpdateRoles(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		request *apb.UpdateRolesRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.UpdateRolesResponse
+		wantErr bool
+	}{
+		{
+			name: "default update roles",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.UpdateRolesRequest{
+					Roles: []*apb.Role{
+						{
+							Id:   adminRoleID,
+							Name: "New Name",
+						},
+					},
+				},
+			},
+			want: &apb.UpdateRolesResponse{
+				Status: apb.Status_STATUS_OK,
+			},
+			wantErr: false,
+		},
+		{
+			name: "error update roles",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.UpdateRolesRequest{
+					Roles: []*apb.Role{
+						{
+							Id:   uuid.NewString(),
+							Name: "New Name",
+						},
+					},
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.UpdateRoles(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.UpdateRoles() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != nil && got.Status != tt.want.Status {
+				t.Errorf("Auth.UpdateRoles() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestAuth_DeletePermissionsForRole(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		request *apb.DeletePermissionsForRoleRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.DeletePermissionsForRoleResponse
+		wantErr bool
+	}{
+		{
+			name: "default delete permissions for role",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.DeletePermissionsForRoleRequest{
+					RoleName: "adminTestRole",
+					PermissionsToDelete: []string{
+						"/gosdn.core.CoreService/GetPnd",
+						"/gosdn.core.CoreService/GetPndList",
+					},
+				},
+			},
+			want: &apb.DeletePermissionsForRoleResponse{
+				Status: apb.Status_STATUS_OK,
+			},
+			wantErr: false,
+		},
+		{
+			name: "error delete permissions for role no proper permissions provided",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.DeletePermissionsForRoleRequest{
+					RoleName: "adminTestRole",
+					PermissionsToDelete: []string{
+						"no",
+					},
+				},
+			},
+			want: &apb.DeletePermissionsForRoleResponse{
+				Status: apb.Status_STATUS_OK,
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.DeletePermissionsForRole(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.DeletePermissionsForRole() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != nil && got.Status != tt.want.Status {
+				t.Errorf("Auth.DeletePermissionsForRole() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestAuth_DeleteRoles(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		request *apb.DeleteRolesRequest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *apb.DeleteRolesResponse
+		wantErr bool
+	}{
+		{
+			name: "default delete roles",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.DeleteRolesRequest{
+					RoleName: []string{
+						"userTestRole",
+					},
+				},
+			},
+			want: &apb.DeleteRolesResponse{
+				Status: apb.Status_STATUS_OK,
+			},
+			wantErr: false,
+		},
+		{
+			name: "error delete roles",
+			args: args{
+				ctx: context.TODO(),
+				request: &apb.DeleteRolesRequest{
+					RoleName: []string{
+						"no",
+					},
+				},
+			},
+			want: &apb.DeleteRolesResponse{
+				Status: apb.Status_STATUS_OK,
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := Auth{}
+			got, err := s.DeleteRoles(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Auth.DeleteRoles() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != nil && got.Status != tt.want.Status {
+				t.Errorf("Auth.DeleteRoles() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func clearAndCreateAuthTestSetup() error {
+	//clear setup if changed
+	storedUsers, err := userc.GetAll()
+	if err != nil {
+		return err
+	}
+	for _, u := range storedUsers {
+		err = userc.Delete(u)
+		if err != nil {
+			return err
+		}
+	}
+
+	storedRoles, err := rolec.GetAll()
+	if err != nil {
+		return err
+	}
+	for _, r := range storedRoles {
+		err = rolec.Delete(r)
+		if err != nil {
+			return err
+		}
+	}
+
+	// create dataset
+	err = createTestUsers()
+	if err != nil {
+		return err
+	}
+
+	err = createTestRoles()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+//TODO(faseid): change password to hashed/encrypted one
+func createTestUsers() error {
+	randomRoleMap := map[string]string{pndID: randomRoleName}
+
+	users := []rbac.User{
+		{UserID: uuid.MustParse(adminID), UserName: "testAdmin", Roles: adminRoleMap, Password: "admin"},
+		{UserID: uuid.MustParse(userID), UserName: "testUser", Roles: userRoleMap, Password: "user"},
+		{UserID: uuid.New(), UserName: "testRandom", Roles: randomRoleMap, Password: "aurelius", Token: "wrong token"},
+	}
+
+	for _, u := range users {
+		err := userc.Add(rbac.NewUser(u.ID(), u.Name(), u.Roles, u.Password, ""))
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func createTestRoles() error {
+	roles := []rbac.Role{
+		{
+			RoleID:      uuid.MustParse(adminRoleID),
+			RoleName:    "adminTestRole",
+			Description: "Admin",
+			Permissions: []string{
+				"/gosdn.core.CoreService/GetPnd",
+				"/gosdn.core.CoreService/GetPndList",
+			},
+		},
+		{
+			RoleID:      uuid.New(),
+			RoleName:    "userTestRole",
+			Description: "User",
+			Permissions: []string{
+				"/gosdn.pnd.PndService/GetChangeList",
+			},
+		},
+		{
+			RoleID:      uuid.New(),
+			RoleName:    randomRoleName,
+			Description: "Not a role",
+			Permissions: []string{
+				"nope",
+			},
+		},
+	}
+
+	for _, r := range roles {
+		err := rolec.Add(rbac.NewRole(r.ID(), r.Name(), r.Description, r.Permissions))
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// This is needed as a workaround for a bug where the output of the getUser test falsely was
+// that it failed while actually passing. Apparantely, this can happen when loggers write
+// the output of test cases.
+// Solution found here: https://github.com/gotestyourself/gotestsum/issues/141#issuecomment-686243110
+func patchLogger(t *testing.T) {
+	orig := log.Writer()
+	buf := new(bytes.Buffer)
+	log.SetOutput(buf)
+
+	t.Cleanup(func() {
+		// optionally check t.Failed here if you only want to print logs on failure
+
+		t.Log(buf.String())
+		log.SetOutput(orig)
+	})
+}
diff --git a/controller/northbound/server/nbi.go b/controller/northbound/server/nbi.go
index 38230e7f0f787d82ce1ac6fc8e0fbc1ba71d31da..35fa92ab2fe6824bc653ba99c1e625238cf2c36a 100644
--- a/controller/northbound/server/nbi.go
+++ b/controller/northbound/server/nbi.go
@@ -2,6 +2,7 @@ package server
 
 import (
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
 	"code.fbi.h-da.de/danet/gosdn/controller/metrics"
 	"github.com/prometheus/client_golang/prometheus"
 	log "github.com/sirupsen/logrus"
@@ -10,6 +11,8 @@ import (
 )
 
 var pndc networkdomain.PndStore
+var userc rbac.UserService
+var rolec rbac.RoleService
 
 // NorthboundInterface is the representation of the
 // gRPC services used provided.
@@ -22,8 +25,10 @@ type NorthboundInterface struct {
 }
 
 // NewNBI receives a PndStore and returns a new gRPC *NorthboundInterface
-func NewNBI(pnds networkdomain.PndStore) *NorthboundInterface {
+func NewNBI(pnds networkdomain.PndStore, users rbac.UserService, roles rbac.RoleService) *NorthboundInterface {
 	pndc = pnds
+	userc = users
+	rolec = roles
 	return &NorthboundInterface{
 		Pnd:  &pndServer{},
 		Core: &core{},
diff --git a/controller/northbound/server/pnd_test.go b/controller/northbound/server/pnd_test.go
index b2acf2d7f3ddcbaa68d7f3dc2b9743850adb45d7..6fa7ea4064a2fd9b6b60c6777cb9c344797b60df 100644
--- a/controller/northbound/server/pnd_test.go
+++ b/controller/northbound/server/pnd_test.go
@@ -15,6 +15,7 @@ import (
 	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
 	"code.fbi.h-da.de/danet/gosdn/controller/mocks"
 	"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
+	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
 	"code.fbi.h-da.de/danet/yang-models/generated/openconfig"
 	"github.com/golang/protobuf/proto"
 	"github.com/google/go-cmp/cmp"
@@ -44,6 +45,7 @@ var committedChangeUUID uuid.UUID
 var deviceUUID uuid.UUID
 var mockPnd *mocks.NetworkDomain
 var mockDevice device.Device
+var mockJwt *rbac.JWTManager
 var sbiStore southbound.Store
 
 func callback(id uuid.UUID, ch chan device.Details) {
@@ -147,6 +149,15 @@ func TestMain(m *testing.M) {
 		log.Fatal(err)
 	}
 
+	// everyting auth related
+	userc = rbac.NewUserService(rbac.NewMemoryUserStore())
+	rolec = rbac.NewRoleService(rbac.NewMemoryRoleStore())
+	err = clearAndCreateAuthTestSetup()
+	if err != nil {
+		log.Fatal(err)
+	}
+	jwt = rbac.NewJWTManager("", 1*time.Minute)
+
 	os.Exit(m.Run())
 }
 
diff --git a/controller/nucleus/databaseSbiStore.go b/controller/nucleus/databaseSbiStore.go
index 4c1ebe70f8226538a578e1a9165f02a50b4384c2..484311007e357dd6ee7858be8bd305fef460f6a8 100644
--- a/controller/nucleus/databaseSbiStore.go
+++ b/controller/nucleus/databaseSbiStore.go
@@ -10,7 +10,6 @@ import (
 	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/mongo"
 
-	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -109,32 +108,6 @@ func (s *DatabaseSbiStore) GetAll() ([]southbound.LoadedSbi, error) {
 	return loadedSbis, nil
 }
 
-// GetSBI takes a SouthboundInterface's UUID or name and returns the SouthboundInterface. If the requested
-// SouthboundInterface does not exist an error is returned.
-func (s *DatabaseSbiStore) GetSBI(id uuid.UUID) (southbound.LoadedSbi, error) {
-	var loadedSbi southbound.LoadedSbi
-
-	client, ctx, cancel := database.GetMongoConnection()
-	defer cancel()
-	defer client.Disconnect(ctx)
-
-	db := client.Database(database.DatabaseName)
-	collection := db.Collection(s.sbiStoreName)
-	result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: id.String()}})
-	if result == nil {
-		return loadedSbi, nil
-	}
-
-	err := result.Decode(&loadedSbi)
-	if err != nil {
-		log.Printf("Failed marshalling %v", err)
-
-		return loadedSbi, errors.ErrCouldNotMarshall{StoreName: sbiStoreName}
-	}
-
-	return loadedSbi, nil
-}
-
 func getSbiTypeFromString(sbiTypeAsString string) spb.Type {
 	sbiTypeInt := spb.Type_value[sbiTypeAsString]
 
diff --git a/controller/rbac/databaseRoleStore.go b/controller/rbac/databaseRoleStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..4ee86a6c0bf92cf6fbb464efb5640f05c60f73ff
--- /dev/null
+++ b/controller/rbac/databaseRoleStore.go
@@ -0,0 +1,179 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/database"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+	"github.com/google/uuid"
+	log "github.com/sirupsen/logrus"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+// DatabaseRoleStore is used to store roles in database
+type DatabaseRoleStore struct {
+	roleStoreName string
+}
+
+// Add adds a Role.
+func (s *DatabaseRoleStore) Add(roleToAdd rbac.Role) error {
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	_, err := client.Database(database.DatabaseName).
+		Collection(s.roleStoreName).
+		InsertOne(ctx, roleToAdd)
+	if err != nil {
+		if mongo.IsDuplicateKeyError(err) {
+			return nil
+		}
+
+		return errors.ErrCouldNotCreate{StoreName: s.roleStoreName}
+	}
+
+	return nil
+}
+
+// Delete deletes a Role.
+func (s *DatabaseRoleStore) Delete(roleToDelete rbac.Role) error {
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	_, err := client.Database(database.DatabaseName).
+		Collection(s.roleStoreName).
+		DeleteOne(ctx, bson.D{primitive.E{Key: "_id", Value: roleToDelete.ID().String()}})
+	if err != nil {
+		return errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+	}
+
+	return nil
+}
+
+// Get takes a Roles's UUID or name and returns the Role. If the requested
+// Role does not exist an error is returned.
+func (s *DatabaseRoleStore) Get(query store.Query) (rbac.LoadedRole, error) {
+	var loadedRole rbac.LoadedRole
+
+	if query.ID != uuid.Nil {
+		loadedRole, err := s.getByID(query.ID)
+		if err != nil {
+			return loadedRole, errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+		}
+
+		return loadedRole, nil
+	}
+
+	loadedRole, err := s.getByName(query.Name)
+	if err != nil {
+		return loadedRole, errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+	}
+
+	return loadedRole, nil
+}
+
+func (s *DatabaseRoleStore) getByID(idOfRole uuid.UUID) (rbac.LoadedRole, error) {
+	var loadedRole rbac.LoadedRole
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	db := client.Database(database.DatabaseName)
+	collection := db.Collection(s.roleStoreName)
+	result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfRole.String()}})
+	if result == nil {
+		return loadedRole, errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+	}
+
+	err := result.Decode(&loadedRole)
+	if err != nil {
+		log.Printf("Failed marshalling %v", err)
+		return loadedRole, errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+	}
+
+	return loadedRole, nil
+}
+
+func (s *DatabaseRoleStore) getByName(nameOfRole string) (rbac.LoadedRole, error) {
+	var loadedRole rbac.LoadedRole
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	db := client.Database(database.DatabaseName)
+	collection := db.Collection(s.roleStoreName)
+	result := collection.FindOne(ctx, bson.D{primitive.E{Key: "rolename", Value: nameOfRole}})
+	if result == nil {
+		return loadedRole, errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+	}
+
+	err := result.Decode(&loadedRole)
+	if err != nil {
+		log.Printf("Failed marshalling %v", err)
+		return loadedRole, errors.ErrCouldNotFind{StoreName: s.roleStoreName}
+	}
+
+	return loadedRole, nil
+}
+
+// GetAll returns all Roles.
+func (s *DatabaseRoleStore) GetAll() ([]rbac.LoadedRole, error) {
+	var loadedRoles []rbac.LoadedRole
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+	db := client.Database(database.DatabaseName)
+	collection := db.Collection(s.roleStoreName)
+
+	cursor, err := collection.Find(ctx, bson.D{})
+	if err != nil {
+		return nil, err
+	}
+	defer cursor.Close(ctx)
+
+	err = cursor.All(ctx, &loadedRoles)
+	if err != nil {
+		log.Printf("Failed marshalling %v", err)
+
+		return nil, errors.ErrCouldNotMarshall{StoreName: s.roleStoreName}
+	}
+	return loadedRoles, nil
+}
+
+// Update updates the role
+func (s *DatabaseRoleStore) Update(roleToUpdate rbac.Role) error {
+	var updatedLoadedRole rbac.LoadedRole
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	update := bson.D{primitive.E{Key: "$set", Value: roleToUpdate}}
+
+	upsert := false
+	after := options.After
+	opt := options.FindOneAndUpdateOptions{
+		Upsert:         &upsert,
+		ReturnDocument: &after,
+	}
+
+	err := client.Database(database.DatabaseName).
+		Collection(s.roleStoreName).
+		FindOneAndUpdate(
+			ctx, bson.M{"_id": roleToUpdate.ID().String()}, update, &opt).
+		Decode(&updatedLoadedRole)
+	if err != nil {
+		log.Printf("Could not update Role: %v", err)
+
+		return errors.ErrCouldNotUpdate{StoreName: s.roleStoreName}
+	}
+
+	return nil
+}
diff --git a/controller/rbac/databaseUserStore.go b/controller/rbac/databaseUserStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..f6b0064c391059def226dc47212f953a29b039ee
--- /dev/null
+++ b/controller/rbac/databaseUserStore.go
@@ -0,0 +1,179 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/database"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+	"github.com/google/uuid"
+	log "github.com/sirupsen/logrus"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+// DatabaseUserStore is used to store users in database
+type DatabaseUserStore struct {
+	userStoreName string
+}
+
+// Add adds an User.
+func (s *DatabaseUserStore) Add(userToAdd rbac.User) error {
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	_, err := client.Database(database.DatabaseName).
+		Collection(s.userStoreName).
+		InsertOne(ctx, userToAdd)
+	if err != nil {
+		if mongo.IsDuplicateKeyError(err) {
+			return nil
+		}
+
+		return errors.ErrCouldNotCreate{StoreName: s.userStoreName}
+	}
+
+	return nil
+}
+
+// Delete deletes an User.
+func (s *DatabaseUserStore) Delete(userToDelete rbac.User) error {
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	_, err := client.Database(database.DatabaseName).
+		Collection(s.userStoreName).
+		DeleteOne(ctx, bson.D{primitive.E{Key: "_id", Value: userToDelete.ID().String()}})
+	if err != nil {
+		return errors.ErrCouldNotFind{StoreName: s.userStoreName}
+	}
+
+	return nil
+}
+
+// Get takes a User's UUID or name and returns the User. If the requested
+// User does not exist an error is returned.
+func (s *DatabaseUserStore) Get(query store.Query) (rbac.LoadedUser, error) {
+	var loadedUser rbac.LoadedUser
+
+	if query.ID != uuid.Nil {
+		loadedUser, err := s.getByID(query.ID)
+		if err != nil {
+			return loadedUser, errors.ErrCouldNotFind{StoreName: s.userStoreName}
+		}
+
+		return loadedUser, nil
+	}
+
+	loadedUser, err := s.getByName(query.Name)
+	if err != nil {
+		return loadedUser, errors.ErrCouldNotFind{StoreName: s.userStoreName}
+	}
+
+	return loadedUser, nil
+}
+
+func (s *DatabaseUserStore) getByID(idOfUser uuid.UUID) (rbac.LoadedUser, error) {
+	var loadedUser rbac.LoadedUser
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	db := client.Database(database.DatabaseName)
+	collection := db.Collection(s.userStoreName)
+	result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfUser.String()}})
+	if result == nil {
+		return loadedUser, errors.ErrCouldNotFind{StoreName: s.userStoreName}
+	}
+
+	err := result.Decode(&loadedUser)
+	if err != nil {
+		log.Printf("Failed marshalling %v", err)
+		return loadedUser, errors.ErrCouldNotFind{StoreName: s.userStoreName}
+	}
+
+	return loadedUser, nil
+}
+
+func (s *DatabaseUserStore) getByName(nameOfUser string) (rbac.LoadedUser, error) {
+	var loadedUser rbac.LoadedUser
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	db := client.Database(database.DatabaseName)
+	collection := db.Collection(s.userStoreName)
+	result := collection.FindOne(ctx, bson.D{primitive.E{Key: "username", Value: nameOfUser}})
+	if result == nil {
+		return loadedUser, errors.ErrCouldNotFind{StoreName: s.userStoreName}
+	}
+
+	err := result.Decode(&loadedUser)
+	if err != nil {
+		log.Printf("Failed marshalling %v", err)
+		return loadedUser, errors.ErrCouldNotFind{StoreName: s.userStoreName}
+	}
+
+	return loadedUser, nil
+}
+
+// GetAll returns all Users
+func (s *DatabaseUserStore) GetAll() ([]rbac.LoadedUser, error) {
+	var loadedUsers []rbac.LoadedUser
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+	db := client.Database(database.DatabaseName)
+	collection := db.Collection(s.userStoreName)
+
+	cursor, err := collection.Find(ctx, bson.D{})
+	if err != nil {
+		return nil, err
+	}
+	defer cursor.Close(ctx)
+
+	err = cursor.All(ctx, &loadedUsers)
+	if err != nil {
+		log.Printf("Failed marshalling %v", err)
+
+		return nil, errors.ErrCouldNotMarshall{StoreName: s.userStoreName}
+	}
+	return loadedUsers, nil
+}
+
+// Update updates the User.
+func (s *DatabaseUserStore) Update(userToUpdate rbac.User) error {
+	var updatedLoadedUser rbac.LoadedUser
+
+	client, ctx, cancel := database.GetMongoConnection()
+	defer cancel()
+	defer client.Disconnect(ctx)
+
+	update := bson.D{primitive.E{Key: "$set", Value: userToUpdate}}
+
+	upsert := false
+	after := options.After
+	opt := options.FindOneAndUpdateOptions{
+		Upsert:         &upsert,
+		ReturnDocument: &after,
+	}
+
+	err := client.Database(database.DatabaseName).
+		Collection(s.userStoreName).
+		FindOneAndUpdate(
+			ctx, bson.M{"_id": userToUpdate.ID().String()}, update, &opt).
+		Decode(&updatedLoadedUser)
+	if err != nil {
+		log.Printf("Could not update User: %v", err)
+
+		return errors.ErrCouldNotUpdate{StoreName: s.userStoreName}
+	}
+
+	return nil
+}
diff --git a/controller/rbac/jwtManager.go b/controller/rbac/jwtManager.go
index 40eae49248690dfa71dbe504e1349b98ed10bb6d..981310821ac90086a0ddf8078b3b06d43b9bc1d6 100644
--- a/controller/rbac/jwtManager.go
+++ b/controller/rbac/jwtManager.go
@@ -31,7 +31,7 @@ type UserClaims struct {
 func (man *JWTManager) GenerateToken(user User) (string, error) {
 	claims := UserClaims{
 		StandardClaims: jwt.StandardClaims{ExpiresAt: time.Now().Add(man.tokenDuration).Unix()},
-		Username:       user.GetName(),
+		Username:       user.Name(),
 	}
 
 	token := jwt.NewWithClaims(signingMethod, claims)
diff --git a/controller/rbac/jwtManager_test.go b/controller/rbac/jwtManager_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e79ec1b9ae4824066cf829b9c617fc5376f4ba1b
--- /dev/null
+++ b/controller/rbac/jwtManager_test.go
@@ -0,0 +1,110 @@
+package rbac
+
+import (
+	"reflect"
+	"testing"
+	"time"
+
+	"github.com/golang-jwt/jwt"
+)
+
+func TestJWTManager_GenerateToken(t *testing.T) {
+	type fields struct {
+		secretKey     string
+		tokenDuration time.Duration
+	}
+	type args struct {
+		user User
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    string
+		wantErr bool
+	}{
+		{
+			name: "default generate token",
+			fields: fields{
+				secretKey:     "",
+				tokenDuration: 1 * time.Minute,
+			},
+			args: args{
+				user: User{
+					UserName: "testUser",
+				},
+			},
+			want:    "testUser",
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			man := &JWTManager{
+				secretKey:     tt.fields.secretKey,
+				tokenDuration: tt.fields.tokenDuration,
+			}
+			got, err := man.GenerateToken(tt.args.user)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("JWTManager.GenerateToken() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			gotClaims, _ := man.VerifyToken(got)
+			claimsUser := gotClaims.Username
+
+			if claimsUser != tt.want {
+				t.Errorf("JWTManager.GenerateToken() = %v, want %v", claimsUser, tt.want)
+			}
+		})
+	}
+}
+
+func TestJWTManager_VerifyToken(t *testing.T) {
+	type fields struct {
+		secretKey     string
+		tokenDuration time.Duration
+	}
+	tests := []struct {
+		name     string
+		fields   fields
+		userName string
+		want     *UserClaims
+		wantErr  bool
+	}{
+		{
+			name: "default verify token",
+			fields: fields{
+				secretKey:     "",
+				tokenDuration: 1 * time.Minute,
+			},
+			userName: "testUser",
+			want: &UserClaims{
+				StandardClaims: jwt.StandardClaims{
+					ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
+				},
+				Username: "testUser",
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			man := &JWTManager{
+				secretKey:     tt.fields.secretKey,
+				tokenDuration: tt.fields.tokenDuration,
+			}
+
+			token, err := man.GenerateToken(User{UserName: tt.userName})
+			got, err := man.VerifyToken(token)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("JWTManager.VerifyToken() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("JWTManager.VerifyToken() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/controller/rbac/memoryRoleStore.go b/controller/rbac/memoryRoleStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..e40dc60b79afde6cecb0a69e9586f140fe74fbd9
--- /dev/null
+++ b/controller/rbac/memoryRoleStore.go
@@ -0,0 +1,110 @@
+package rbac
+
+import (
+	"encoding/json"
+
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+)
+
+//MemoryRoleStore provides a in-memory implementation for roles.
+type MemoryRoleStore struct {
+	Store           map[string]rbac.LoadedRole
+	nameLookupTable map[string]string
+}
+
+// NewMemoryRoleStore returns a specific in-memory store for roles.
+func NewMemoryRoleStore() rbac.RoleStore {
+	return &MemoryRoleStore{
+		Store:           make(map[string]rbac.LoadedRole),
+		nameLookupTable: make(map[string]string),
+	}
+}
+
+// Add adds a item to the store.
+func (s *MemoryRoleStore) Add(item rbac.Role) error {
+	var role rbac.LoadedRole
+
+	b, err := json.Marshal(item)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(b, &role)
+	if err != nil {
+		return err
+	}
+
+	_, ok := s.Store[role.ID]
+	if ok {
+		return nil
+	}
+
+	s.Store[role.ID] = role
+	s.nameLookupTable[item.Name()] = role.ID
+
+	return nil
+}
+
+// Delete deletes a role from the role store.
+func (s *MemoryRoleStore) Delete(item rbac.Role) error {
+	delete(s.Store, item.ID().String())
+	return nil
+}
+
+// Update updates an existing role.
+func (s *MemoryRoleStore) Update(item rbac.Role) error {
+	_, ok := s.Store[item.ID().String()]
+	if ok {
+		return nil
+	}
+
+	var role rbac.LoadedRole
+
+	b, err := json.Marshal(item)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(b, &role)
+	if err != nil {
+		return err
+	}
+
+	s.Store[item.ID().String()] = role
+	s.nameLookupTable[item.Name()] = item.ID().String()
+
+	return nil
+}
+
+// Get takes a role's UUID or name and returns the role.
+func (s *MemoryRoleStore) Get(query store.Query) (rbac.LoadedRole, error) {
+	// First search for direct hit on UUID.
+	item, ok := s.Store[query.ID.String()]
+	if !ok {
+		// Second search for name
+		id, ok := s.nameLookupTable[query.Name]
+		if !ok {
+			return item, errors.ErrCouldNotFind{StoreName: roleStoreName}
+		}
+
+		item, ok := s.Store[id]
+		if !ok {
+			return item, errors.ErrCouldNotFind{StoreName: roleStoreName}
+		}
+
+		return item, nil
+	}
+
+	return item, nil
+}
+
+// GetAll returns all stored roles.
+func (s *MemoryRoleStore) GetAll() ([]rbac.LoadedRole, error) {
+	var allItems []rbac.LoadedRole
+
+	for _, item := range s.Store {
+		allItems = append(allItems, item)
+	}
+
+	return allItems, nil
+}
diff --git a/controller/rbac/memoryUserStore.go b/controller/rbac/memoryUserStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..338f56180fe137187b9c12a95530bf5a1caf5d46
--- /dev/null
+++ b/controller/rbac/memoryUserStore.go
@@ -0,0 +1,110 @@
+package rbac
+
+import (
+	"encoding/json"
+
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+)
+
+//MemoryUserStore provides a in-memory implementation for users.
+type MemoryUserStore struct {
+	Store           map[string]rbac.LoadedUser
+	nameLookupTable map[string]string
+}
+
+// NewMemoryUserStore returns a specific in-memory store for users.
+func NewMemoryUserStore() rbac.UserStore {
+	return &MemoryUserStore{
+		Store:           make(map[string]rbac.LoadedUser),
+		nameLookupTable: make(map[string]string),
+	}
+}
+
+// Add adds a item to the store.
+func (s *MemoryUserStore) Add(item rbac.User) error {
+	var user rbac.LoadedUser
+
+	b, err := json.Marshal(item)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(b, &user)
+	if err != nil {
+		return err
+	}
+
+	_, ok := s.Store[user.ID]
+	if ok {
+		return nil
+	}
+
+	s.Store[user.ID] = user
+	s.nameLookupTable[item.Name()] = user.ID
+
+	return nil
+}
+
+// Delete deletes a user from the user store.
+func (s *MemoryUserStore) Delete(item rbac.User) error {
+	delete(s.Store, item.ID().String())
+	return nil
+}
+
+// Update updates an existing user.
+func (s *MemoryUserStore) Update(item rbac.User) error {
+	_, ok := s.Store[item.ID().String()]
+	if ok {
+		return nil
+	}
+
+	var user rbac.LoadedUser
+
+	b, err := json.Marshal(item)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(b, &user)
+	if err != nil {
+		return err
+	}
+
+	s.Store[item.ID().String()] = user
+	s.nameLookupTable[item.Name()] = item.ID().String()
+
+	return nil
+}
+
+// Get takes a user's UUID or name and returns the user.
+func (s *MemoryUserStore) Get(query store.Query) (rbac.LoadedUser, error) {
+	// First search for direct hit on UUID.
+	item, ok := s.Store[query.ID.String()]
+	if !ok {
+		// Second search for name
+		id, ok := s.nameLookupTable[query.Name]
+		if !ok {
+			return item, errors.ErrCouldNotFind{StoreName: userStoreName}
+		}
+
+		item, ok := s.Store[id]
+		if !ok {
+			return item, errors.ErrCouldNotFind{StoreName: userStoreName}
+		}
+
+		return item, nil
+	}
+
+	return item, nil
+}
+
+// GetAll returns all stored users.
+func (s *MemoryUserStore) GetAll() ([]rbac.LoadedUser, error) {
+	var allItems []rbac.LoadedUser
+
+	for _, item := range s.Store {
+		allItems = append(allItems, item)
+	}
+
+	return allItems, nil
+}
diff --git a/controller/rbac/rbacService.go b/controller/rbac/rbacService.go
new file mode 100644
index 0000000000000000000000000000000000000000..e6f000c3d246d35030de8783496a1a5d22f9c124
--- /dev/null
+++ b/controller/rbac/rbacService.go
@@ -0,0 +1,151 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+	"github.com/google/uuid"
+)
+
+//UserService provides a user service implementation.
+type UserService struct {
+	userStore rbac.UserStore
+}
+
+// NewUserService creates a user service.
+func NewUserService(userStore rbac.UserStore) rbac.UserService {
+	return &UserService{
+		userStore: userStore,
+	}
+}
+
+// Add adds a user to the user store.
+func (s *UserService) Add(userToAdd rbac.User) error {
+	err := s.userStore.Add(userToAdd)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Delete deletes a user from the user store.
+func (s *UserService) Delete(userToDelete rbac.User) error {
+	err := s.userStore.Delete(userToDelete)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Update updates a existing user.
+func (s *UserService) Update(userToUpdate rbac.User) error {
+	err := s.userStore.Update(userToUpdate)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Get takes a user's UUID or name and returns the user.
+func (s *UserService) Get(query store.Query) (rbac.User, error) {
+	loadedUser, err := s.userStore.Get(query)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.createUserFromStore(loadedUser), nil
+}
+
+// GetAll returns all stored users.
+func (s *UserService) GetAll() ([]rbac.User, error) {
+	var users []rbac.User
+
+	loadedUsers, err := s.userStore.GetAll()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, loadedUser := range loadedUsers {
+		users = append(users, s.createUserFromStore(loadedUser))
+	}
+
+	return users, nil
+}
+
+func (s *UserService) createUserFromStore(loadedUser rbac.LoadedUser) rbac.User {
+	return NewUser(uuid.MustParse(loadedUser.ID), loadedUser.UserName, loadedUser.Roles, loadedUser.Password, loadedUser.Token)
+}
+
+//RoleService provides a role service implementation.
+type RoleService struct {
+	roleStore rbac.RoleStore
+}
+
+// NewRoleService creates a role service.
+func NewRoleService(roleStore rbac.RoleStore) rbac.RoleService {
+	return &RoleService{
+		roleStore: roleStore,
+	}
+}
+
+// Add adds a role to the role store.
+func (s *RoleService) Add(roleToAdd rbac.Role) error {
+	err := s.roleStore.Add(roleToAdd)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Delete deletes a role from the role store.
+func (s *RoleService) Delete(roleToDelete rbac.Role) error {
+	err := s.roleStore.Delete(roleToDelete)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Update updates a existing role.
+func (s *RoleService) Update(roleToUpdate rbac.Role) error {
+	err := s.roleStore.Update(roleToUpdate)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Get takes a roles's UUID or name and returns the role.
+func (s *RoleService) Get(query store.Query) (rbac.Role, error) {
+	loadedRole, err := s.roleStore.Get(query)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.createRoleFromStore(loadedRole), nil
+}
+
+// GetAll returns all stored roles.
+func (s *RoleService) GetAll() ([]rbac.Role, error) {
+	var roles []rbac.Role
+
+	loadedRoles, err := s.roleStore.GetAll()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, loadedRole := range loadedRoles {
+		roles = append(roles, s.createRoleFromStore(loadedRole))
+	}
+
+	return roles, nil
+}
+
+func (s *RoleService) createRoleFromStore(loadedRole rbac.LoadedRole) rbac.Role {
+	return NewRole(uuid.MustParse(loadedRole.ID), loadedRole.RoleName, loadedRole.Description, loadedRole.Permissions)
+}
diff --git a/controller/rbac/role.go b/controller/rbac/role.go
new file mode 100644
index 0000000000000000000000000000000000000000..a506247ac504f5a56c8013a85a0831da297512c0
--- /dev/null
+++ b/controller/rbac/role.go
@@ -0,0 +1,96 @@
+package rbac
+
+import (
+	"encoding/json"
+
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"github.com/google/uuid"
+	"go.mongodb.org/mongo-driver/bson"
+)
+
+// Role represents the data of a role used for access control and is stored in a storage.
+type Role struct {
+	RoleID      uuid.UUID `json:"_id"`
+	RoleName    string    `json:"rolename"`
+	Description string    `json:"description,omitempty"`
+	Permissions []string  `json:"permissions,omitempty"`
+}
+
+// NewRole creates a new role.
+func NewRole(id uuid.UUID,
+	name string,
+	description string,
+	permissions []string) rbac.Role {
+	return &Role{
+		RoleID:      id,
+		RoleName:    name,
+		Description: description,
+		Permissions: permissions,
+	}
+}
+
+// ID returns a UUID of the role.
+func (r Role) ID() uuid.UUID {
+	return r.RoleID
+}
+
+// Name returns the name of the role.
+func (r Role) Name() string {
+	return r.RoleName
+}
+
+// GetDescription returns the description of the role.
+func (r *Role) GetDescription() string {
+	return r.Description
+}
+
+// GetPermissions returns the permissions linked to the role.
+func (r Role) GetPermissions() []string {
+	return r.Permissions
+}
+
+// RemovePermissionsFromRole takes permissions that should be removed from a role and updates the current permissions accordingly
+func (r *Role) RemovePermissionsFromRole(permissionsToRemove []string) {
+	for _, permToRemove := range permissionsToRemove {
+		r.removePermissionFromRoles(permToRemove)
+	}
+}
+
+func (r *Role) removePermissionFromRoles(permToRemove string) {
+	for i, perm := range r.Permissions {
+		if perm == permToRemove {
+			r.Permissions = append(r.Permissions[:i], r.Permissions[i+1:]...)
+			break
+		}
+	}
+}
+
+// MarshalJSON implements the MarshalJSON interface to store a role as JSON
+func (r *Role) MarshalJSON() ([]byte, error) {
+	return json.Marshal(&struct {
+		RoleID      uuid.UUID `json:"_id"`
+		RoleName    string    `json:"rolename"`
+		Description string    `json:"description,omitempty"`
+		Permissions []string  `json:"permissions,omitempty"`
+	}{
+		RoleID:      r.ID(),
+		RoleName:    r.Name(),
+		Description: r.Description,
+		Permissions: r.Permissions,
+	})
+}
+
+// MarshalBSON implments the MarshalBSON interface to store a role as BSON
+func (r *Role) MarshalBSON() ([]byte, error) {
+	return bson.Marshal(&struct {
+		RoleID      string   `bson:"_id"`
+		RoleName    string   `bson:"rolename"`
+		Description string   `bson:"description,omitempty"`
+		Permissions []string `bson:"permissions,omitempty"`
+	}{
+		RoleID:      r.ID().String(),
+		RoleName:    r.Name(),
+		Description: r.Description,
+		Permissions: r.Permissions,
+	})
+}
diff --git a/controller/rbac/roleStore.go b/controller/rbac/roleStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..4482168fa331fbe70fbee6830df0283d5e49a194
--- /dev/null
+++ b/controller/rbac/roleStore.go
@@ -0,0 +1,31 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+)
+
+const (
+	roleStoreName = "role"
+)
+
+// RoleStore is used to store Roles
+type RoleStore struct {
+	roleStoreName string
+}
+
+// NewRoleStore returns a roleStore
+func NewRoleStore() rbac.RoleStore {
+	storeMode := store.GetStoreMode()
+
+	switch storeMode {
+	case store.Filesystem:
+		return NewMemoryRoleStore()
+	case store.Database:
+		return &DatabaseRoleStore{"role.json"}
+	case store.Memory:
+		return NewMemoryRoleStore()
+	default:
+		return nil
+	}
+}
diff --git a/controller/rbac/user.go b/controller/rbac/user.go
index 475b403ffff68d5ad18bef5b56759d097fba5f62..0ff1ecd3c4b15723d8cd09aedf6762e61009dbc4 100644
--- a/controller/rbac/user.go
+++ b/controller/rbac/user.go
@@ -1,25 +1,107 @@
 package rbac
 
 import (
+	"encoding/json"
+
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
 	"github.com/google/uuid"
+	"go.mongodb.org/mongo-driver/bson"
 )
 
-// Users represents a set of multiple users.
-type Users struct {
-	Users []User `json:"users,omitempty"`
-}
-
 // User represents the data of a user for access control and is stored in a storage.
 type User struct {
-	ID       uuid.UUID `json:"id,omitempty"`
-	Name     string    `json:"name,omitempty"`
-	Roles    []string  `json:"roles,omitempty"`
-	PndID    uuid.UUID `json:"pndId,omitempty"`
-	Password string    `json:"password,omitempty"`
-	Token    string    `json:"token,omitempty"`
+	UserID   uuid.UUID         `json:"_id"`
+	UserName string            `json:"username"`
+	Roles    map[string]string `json:"roles,omitempty"`
+	Password string            `json:"password"`
+	Token    string            `json:"token,omitempty"`
+}
+
+// NewUser creates a new user.
+func NewUser(id uuid.UUID,
+	name string,
+	roles map[string]string,
+	pw string,
+	token string) rbac.User {
+	return &User{
+		UserID:   id,
+		UserName: name,
+		Roles:    roles,
+		Password: pw,
+		Token:    token,
+	}
+}
+
+// ID returns a UUID of the user.
+func (u *User) ID() uuid.UUID {
+	return u.UserID
+}
+
+// Name returns the name of the user.
+func (u *User) Name() string {
+	return u.UserName
+}
+
+// IsCorrectPassword compares the provided with the stored password of a user.
+func (u *User) IsCorrectPassword(pwd string) bool {
+	return pwd == u.Password
+}
+
+// GetPassword returns the password of the user.
+func (u *User) GetPassword() string {
+	return u.Password
+}
+
+// GetRoles returns the roles of the user.
+func (u *User) GetRoles() map[string]string {
+	return u.Roles
+}
+
+// GetToken returns the token of the user.
+func (u *User) GetToken() string {
+	return u.Token
+}
+
+// SetName sets the name of the user
+func (u *User) SetName(name string) {
+	u.UserName = name
+}
+
+// SetToken sets the token of the user
+func (u *User) SetToken(token string) {
+	u.Token = token
+}
+
+// MarshalJSON implements the MarshalJSON interface to store a user as JSON
+func (u *User) MarshalJSON() ([]byte, error) {
+	return json.Marshal(&struct {
+		UserID   uuid.UUID         `json:"_id"`
+		UserName string            `json:"username"`
+		Roles    map[string]string `json:"roles,omitempty"`
+		Password string            `json:"password"`
+		Token    string            `json:"token,omitempty"`
+	}{
+		UserID:   u.ID(),
+		UserName: u.Name(),
+		Roles:    u.Roles,
+		Password: u.Password,
+		Token:    u.Token,
+	})
 }
 
-// GetName returns the name of the User
-func (u *User) GetName() string {
-	return u.Name
+// MarshalBSON implments the MarshalBSON interface to store a user as BSON
+func (u *User) MarshalBSON() ([]byte, error) {
+	return bson.Marshal(&struct {
+		UserID   string            `bson:"_id"`
+		UserName string            `bson:"username"`
+		Roles    map[string]string `bson:"roles,omitempty"`
+		Password string            `bson:"password"`
+		Token    string            `bson:"token,omitempty"`
+	}{
+		UserID:   u.ID().String(),
+		UserName: u.Name(),
+		Roles:    u.Roles,
+		Password: u.Password,
+		Token:    u.Token,
+	})
 }
diff --git a/controller/rbac/userStore.go b/controller/rbac/userStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..1524abe9c14f286b8fa641e5b3ab8a5ad1df003d
--- /dev/null
+++ b/controller/rbac/userStore.go
@@ -0,0 +1,31 @@
+package rbac
+
+import (
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/store"
+)
+
+const (
+	userStoreName = "user"
+)
+
+// UserStore is used to store Users
+type UserStore struct {
+	userStoreName string
+}
+
+// NewUserStore returns a userStore
+func NewUserStore() rbac.UserStore {
+	storeMode := store.GetStoreMode()
+
+	switch storeMode {
+	case store.Filesystem:
+		return NewMemoryUserStore()
+	case store.Database:
+		return &DatabaseUserStore{"user.json"}
+	case store.Memory:
+		return NewMemoryUserStore()
+	default:
+		return nil
+	}
+}
diff --git a/controller/store/genericStore.go b/controller/store/genericStore.go
index d33e81be6f6202c618334d56e5a18c561c99be6b..cb4c255cf991ea7fbb6f86b5d15717d1c8862b3f 100644
--- a/controller/store/genericStore.go
+++ b/controller/store/genericStore.go
@@ -1,89 +1,87 @@
 package store
 
 import (
-	"reflect"
-	"sync"
-
-	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/store"
-	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
+	"errors"
 
 	"github.com/google/uuid"
-	log "github.com/sirupsen/logrus"
 )
 
-// newGenericStore returns a genericStore
-func newGenericStore() *genericStore {
-	return &genericStore{Store: make(map[uuid.UUID]store.Storable), storeLock: sync.RWMutex{}}
+type storableConstraint interface {
+	ID() uuid.UUID
+	Name() string
 }
 
-type genericStore struct {
-	Store     map[uuid.UUID]store.Storable
-	storeLock sync.RWMutex
+// GenericStore provides a in-memory implementation for multiple stores.
+type GenericStore[T storableConstraint] struct {
+	Store           map[uuid.UUID]T
+	nameLookupTable map[string]uuid.UUID
 }
 
-// Exists takes a Storable's UUID and checks its existence in the store.
-func (s *genericStore) Exists(id uuid.UUID) bool {
-	s.storeLock.RLock()
-	defer s.storeLock.RUnlock()
-	_, ok := s.Store[id]
-	return ok
+// NewGenericStore returns a specific in-memory store for a type T.
+func NewGenericStore[T storableConstraint]() GenericStore[T] {
+	return GenericStore[T]{
+		Store:           make(map[uuid.UUID]T),
+		nameLookupTable: make(map[string]uuid.UUID),
+	}
 }
 
-// Add adds a Storable to the Store
-func (s *genericStore) Add(item store.Storable) error {
-	if s.Exists(item.ID()) {
-		return &errors.ErrAlreadyExists{Item: item}
+func (t *GenericStore[T]) Add(item T) error {
+	_, ok := t.Store[item.ID()]
+	if ok {
+		return errors.New("item not found")
 	}
-	s.storeLock.Lock()
-	s.Store[item.ID()] = item
-	s.storeLock.Unlock()
-	log.WithFields(log.Fields{
-		"type": reflect.TypeOf(item),
-		"uuid": item.ID(),
-	}).Debug("storable was added")
+
+	t.Store[item.ID()] = item
+	t.nameLookupTable[item.Name()] = item.ID()
+
 	return nil
 }
 
-// Get takes a Storable's UUID and returns the Storable. If the requested
-// Storable does not exist an error is returned. Get is only type safe for
-// this Storable interface. For type safe get operations on specialised stores
-// use GetDevice, GetPND, GetSBI, or GetChange respectively.
-func (s *genericStore) Get(id uuid.UUID) (store.Storable, error) {
-	if !s.Exists(id) {
-		return nil, &errors.ErrNotFound{ID: id}
+func (t *GenericStore[T]) Update(item T) error {
+	_, ok := t.Store[item.ID()]
+	if ok {
+		return nil
 	}
-	log.WithFields(log.Fields{
-		"uuid": id,
-	}).Debug("storable was accessed")
-	s.storeLock.RLock()
-	defer s.storeLock.RUnlock()
-	return s.Store[id], nil
+
+	t.Store[item.ID()] = item
+	t.nameLookupTable[item.Name()] = item.ID()
+
+	return nil
 }
 
-// Delete takes a Storable's UUID and deletes it. If the specified UUID does not
-// exist in the Store an error is returned.
-func (s *genericStore) Delete(id uuid.UUID) error {
-	if !s.Exists(id) {
-		return &errors.ErrNotFound{ID: id}
-	}
-	s.storeLock.Lock()
-	delete(s.Store, id)
-	s.storeLock.Unlock()
-	log.WithFields(log.Fields{
-		"uuid": id,
-	}).Debug("storable was deleted")
+func (t *GenericStore[T]) Delete(item T) error {
+	delete(t.Store, item.ID())
+
 	return nil
 }
 
-// UUIDs returns all UUIDs in the store.
-func (s *genericStore) UUIDs() []uuid.UUID {
-	s.storeLock.RLock()
-	defer s.storeLock.RUnlock()
-	keys := make([]uuid.UUID, len(s.Store))
-	i := 0
-	for k := range s.Store {
-		keys[i] = k
-		i++
+func (t *GenericStore[T]) Get(query Query) (T, error) {
+	// First search for direct hit on UUID.
+	item, ok := t.Store[query.ID]
+	if !ok {
+		// Second search for name
+		id, ok := t.nameLookupTable[query.Name]
+		if !ok {
+			return *new(T), errors.New("item not found")
+		}
+
+		item, ok := t.Store[id]
+		if !ok {
+			return *new(T), errors.New("item not found")
+		}
+
+		return item, nil
+	}
+
+	return item, nil
+}
+
+func (t *GenericStore[T]) GetAll() ([]T, error) {
+	var allItems []T
+
+	for _, item := range t.Store {
+		allItems = append(allItems, item)
 	}
-	return keys
+
+	return allItems, nil
 }
diff --git a/controller/store/oldGenericStore.go b/controller/store/oldGenericStore.go
new file mode 100644
index 0000000000000000000000000000000000000000..d33e81be6f6202c618334d56e5a18c561c99be6b
--- /dev/null
+++ b/controller/store/oldGenericStore.go
@@ -0,0 +1,89 @@
+package store
+
+import (
+	"reflect"
+	"sync"
+
+	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/store"
+	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
+
+	"github.com/google/uuid"
+	log "github.com/sirupsen/logrus"
+)
+
+// newGenericStore returns a genericStore
+func newGenericStore() *genericStore {
+	return &genericStore{Store: make(map[uuid.UUID]store.Storable), storeLock: sync.RWMutex{}}
+}
+
+type genericStore struct {
+	Store     map[uuid.UUID]store.Storable
+	storeLock sync.RWMutex
+}
+
+// Exists takes a Storable's UUID and checks its existence in the store.
+func (s *genericStore) Exists(id uuid.UUID) bool {
+	s.storeLock.RLock()
+	defer s.storeLock.RUnlock()
+	_, ok := s.Store[id]
+	return ok
+}
+
+// Add adds a Storable to the Store
+func (s *genericStore) Add(item store.Storable) error {
+	if s.Exists(item.ID()) {
+		return &errors.ErrAlreadyExists{Item: item}
+	}
+	s.storeLock.Lock()
+	s.Store[item.ID()] = item
+	s.storeLock.Unlock()
+	log.WithFields(log.Fields{
+		"type": reflect.TypeOf(item),
+		"uuid": item.ID(),
+	}).Debug("storable was added")
+	return nil
+}
+
+// Get takes a Storable's UUID and returns the Storable. If the requested
+// Storable does not exist an error is returned. Get is only type safe for
+// this Storable interface. For type safe get operations on specialised stores
+// use GetDevice, GetPND, GetSBI, or GetChange respectively.
+func (s *genericStore) Get(id uuid.UUID) (store.Storable, error) {
+	if !s.Exists(id) {
+		return nil, &errors.ErrNotFound{ID: id}
+	}
+	log.WithFields(log.Fields{
+		"uuid": id,
+	}).Debug("storable was accessed")
+	s.storeLock.RLock()
+	defer s.storeLock.RUnlock()
+	return s.Store[id], nil
+}
+
+// Delete takes a Storable's UUID and deletes it. If the specified UUID does not
+// exist in the Store an error is returned.
+func (s *genericStore) Delete(id uuid.UUID) error {
+	if !s.Exists(id) {
+		return &errors.ErrNotFound{ID: id}
+	}
+	s.storeLock.Lock()
+	delete(s.Store, id)
+	s.storeLock.Unlock()
+	log.WithFields(log.Fields{
+		"uuid": id,
+	}).Debug("storable was deleted")
+	return nil
+}
+
+// UUIDs returns all UUIDs in the store.
+func (s *genericStore) UUIDs() []uuid.UUID {
+	s.storeLock.RLock()
+	defer s.storeLock.RUnlock()
+	keys := make([]uuid.UUID, len(s.Store))
+	i := 0
+	for k := range s.Store {
+		keys[i] = k
+		i++
+	}
+	return keys
+}
diff --git a/controller/store/query.go b/controller/store/query.go
new file mode 100644
index 0000000000000000000000000000000000000000..31b198bfd1734d2857da3cacad772ca4dbca98cb
--- /dev/null
+++ b/controller/store/query.go
@@ -0,0 +1,9 @@
+package store
+
+import "github.com/google/uuid"
+
+// Query is used to query objects from stores.
+type Query struct {
+	ID   uuid.UUID
+	Name string
+}
diff --git a/controller/store/utils.go b/controller/store/utils.go
index a3e52bf0a47aea47fefe4c369a3bdaa884ab3ea0..de80337868d0f99061140c791cef1e607efb2bd9 100644
--- a/controller/store/utils.go
+++ b/controller/store/utils.go
@@ -13,12 +13,6 @@ const (
 	pathToStores string = "stores"
 )
 
-// Query is used to query objects from stores.
-type Query struct {
-	ID   uuid.UUID
-	Name string
-}
-
 // FromString is a helper to check if a provided string as a valid UUID or a name.
 func FromString(id string) (uuid.UUID, error) {
 	idAsUUID, err := uuid.Parse(id)
diff --git a/go.mod b/go.mod
index ee4533b6241a51475207a41af4a239d61ef24332..f14f10e3887938957be6e16b645e2b2ab0be637d 100644
--- a/go.mod
+++ b/go.mod
@@ -50,7 +50,7 @@ require (
 	github.com/golang-jwt/jwt v3.2.2+incompatible
 	github.com/golang/glog v1.0.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
-	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/golang/protobuf v1.5.2
 	github.com/golang/snappy v0.0.3 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect