diff --git a/api/go/gosdn/rbac/rbac.pb.go b/api/go/gosdn/rbac/rbac.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..72064841bcbf4a51ce00a0fd237370487c77c816
--- /dev/null
+++ b/api/go/gosdn/rbac/rbac.pb.go
@@ -0,0 +1,1178 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        (unknown)
+// source: gosdn/rbac/rbac.proto
+
+package rbac
+
+import (
+	_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	_ "google.golang.org/protobuf/types/descriptorpb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Status int32
+
+const (
+	Status_STATUS_UNSPECIFIED Status = 0
+	Status_STATUS_OK          Status = 1
+	Status_STATUS_ERROR       Status = 2
+)
+
+// Enum value maps for Status.
+var (
+	Status_name = map[int32]string{
+		0: "STATUS_UNSPECIFIED",
+		1: "STATUS_OK",
+		2: "STATUS_ERROR",
+	}
+	Status_value = map[string]int32{
+		"STATUS_UNSPECIFIED": 0,
+		"STATUS_OK":          1,
+		"STATUS_ERROR":       2,
+	}
+)
+
+func (x Status) Enum() *Status {
+	p := new(Status)
+	*p = x
+	return p
+}
+
+func (x Status) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Status) Descriptor() protoreflect.EnumDescriptor {
+	return file_gosdn_rbac_rbac_proto_enumTypes[0].Descriptor()
+}
+
+func (Status) Type() protoreflect.EnumType {
+	return &file_gosdn_rbac_rbac_proto_enumTypes[0]
+}
+
+func (x Status) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Status.Descriptor instead.
+func (Status) EnumDescriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{0}
+}
+
+// TODO: add additional data to user enum
+type User int32
+
+const (
+	User_USER_UNSPECIFIED User = 0
+	User_USER_NAME        User = 1
+	User_USER_PWD         User = 2 // ...
+)
+
+// Enum value maps for User.
+var (
+	User_name = map[int32]string{
+		0: "USER_UNSPECIFIED",
+		1: "USER_NAME",
+		2: "USER_PWD",
+	}
+	User_value = map[string]int32{
+		"USER_UNSPECIFIED": 0,
+		"USER_NAME":        1,
+		"USER_PWD":         2,
+	}
+)
+
+func (x User) Enum() *User {
+	p := new(User)
+	*p = x
+	return p
+}
+
+func (x User) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (User) Descriptor() protoreflect.EnumDescriptor {
+	return file_gosdn_rbac_rbac_proto_enumTypes[1].Descriptor()
+}
+
+func (User) Type() protoreflect.EnumType {
+	return &file_gosdn_rbac_rbac_proto_enumTypes[1]
+}
+
+func (x User) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use User.Descriptor instead.
+func (User) EnumDescriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{1}
+}
+
+// Login
+type LoginRequest 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,opt,name=username,proto3" json:"username,omitempty"`
+	Pwd       string `protobuf:"bytes,3,opt,name=pwd,proto3" json:"pwd,omitempty"`
+}
+
+func (x *LoginRequest) Reset() {
+	*x = LoginRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LoginRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginRequest) ProtoMessage() {}
+
+func (x *LoginRequest) 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)
+}
+
+// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead.
+func (*LoginRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *LoginRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *LoginRequest) GetUsername() string {
+	if x != nil {
+		return x.Username
+	}
+	return ""
+}
+
+func (x *LoginRequest) GetPwd() string {
+	if x != nil {
+		return x.Pwd
+	}
+	return ""
+}
+
+type LoginResponse 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"`
+	Token     string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"`
+}
+
+func (x *LoginResponse) Reset() {
+	*x = LoginResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LoginResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginResponse) ProtoMessage() {}
+
+func (x *LoginResponse) 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 LoginResponse.ProtoReflect.Descriptor instead.
+func (*LoginResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *LoginResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *LoginResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+func (x *LoginResponse) GetToken() string {
+	if x != nil {
+		return x.Token
+	}
+	return ""
+}
+
+// Logout
+type LogoutRequest 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,opt,name=username,proto3" json:"username,omitempty"`
+}
+
+func (x *LogoutRequest) Reset() {
+	*x = LogoutRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LogoutRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LogoutRequest) ProtoMessage() {}
+
+func (x *LogoutRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[2]
+	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 LogoutRequest.ProtoReflect.Descriptor instead.
+func (*LogoutRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *LogoutRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *LogoutRequest) GetUsername() string {
+	if x != nil {
+		return x.Username
+	}
+	return ""
+}
+
+type LogoutResponse 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 *LogoutResponse) Reset() {
+	*x = LogoutResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LogoutResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LogoutResponse) ProtoMessage() {}
+
+func (x *LogoutResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LogoutResponse.ProtoReflect.Descriptor instead.
+func (*LogoutResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *LogoutResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *LogoutResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// CreateUsers
+type CreateUsersRequest 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"`
+}
+
+func (x *CreateUsersRequest) Reset() {
+	*x = CreateUsersRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CreateUsersRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateUsersRequest) ProtoMessage() {}
+
+func (x *CreateUsersRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[4]
+	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 CreateUsersRequest.ProtoReflect.Descriptor instead.
+func (*CreateUsersRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *CreateUsersRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *CreateUsersRequest) GetUser() []User {
+	if x != nil {
+		return x.User
+	}
+	return nil
+}
+
+type CreateUsersResponse 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 *CreateUsersResponse) Reset() {
+	*x = CreateUsersResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CreateUsersResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateUsersResponse) ProtoMessage() {}
+
+func (x *CreateUsersResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[5]
+	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 CreateUsersResponse.ProtoReflect.Descriptor instead.
+func (*CreateUsersResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *CreateUsersResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *CreateUsersResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+// 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"`
+	Token     string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
+}
+
+func (x *GetUsersRequest) Reset() {
+	*x = GetUsersRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetUsersRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetUsersRequest) ProtoMessage() {}
+
+func (x *GetUsersRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[6]
+	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 GetUsersRequest.ProtoReflect.Descriptor instead.
+func (*GetUsersRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *GetUsersRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetUsersRequest) GetToken() string {
+	if x != nil {
+		return x.Token
+	}
+	return ""
+}
+
+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"`
+	User      []User `protobuf:"varint,3,rep,packed,name=user,proto3,enum=gosdn.rbac.User" json:"user,omitempty"`
+}
+
+func (x *GetUsersResponse) Reset() {
+	*x = GetUsersResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetUsersResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetUsersResponse) ProtoMessage() {}
+
+func (x *GetUsersResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_gosdn_rbac_rbac_proto_msgTypes[7]
+	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 GetUsersResponse.ProtoReflect.Descriptor instead.
+func (*GetUsersResponse) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *GetUsersResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetUsersResponse) GetStatus() Status {
+	if x != nil {
+		return x.Status
+	}
+	return Status_STATUS_UNSPECIFIED
+}
+
+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"`
+	User      []User `protobuf:"varint,2,rep,packed,name=user,proto3,enum=gosdn.rbac.User" json:"user,omitempty"`
+}
+
+func (x *UpdateUsersRequest) Reset() {
+	*x = UpdateUsersRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_gosdn_rbac_rbac_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UpdateUsersRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UpdateUsersRequest) ProtoMessage() {}
+
+func (x *UpdateUsersRequest) 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 {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UpdateUsersRequest.ProtoReflect.Descriptor instead.
+func (*UpdateUsersRequest) Descriptor() ([]byte, []int) {
+	return file_gosdn_rbac_rbac_proto_rawDescGZIP(), []int{8}
+}
+
+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[9]
+		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[9]
+	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{9}
+}
+
+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[10]
+		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[10]
+	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{10}
+}
+
+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[11]
+		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[11]
+	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{11}
+}
+
+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
+}
+
+var File_gosdn_rbac_rbac_proto protoreflect.FileDescriptor
+
+var file_gosdn_rbac_rbac_proto_rawDesc = []byte{
+	0x0a, 0x15, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2f, 0x72, 0x62, 0x61, 0x63, 0x2f, 0x72, 0x62, 0x61,
+	0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72,
+	0x62, 0x61, 0x63, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
+	0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72,
+	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,
+	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, 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,
+	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,
+	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,
+	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,
+	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,
+}
+
+var (
+	file_gosdn_rbac_rbac_proto_rawDescOnce sync.Once
+	file_gosdn_rbac_rbac_proto_rawDescData = file_gosdn_rbac_rbac_proto_rawDesc
+)
+
+func file_gosdn_rbac_rbac_proto_rawDescGZIP() []byte {
+	file_gosdn_rbac_rbac_proto_rawDescOnce.Do(func() {
+		file_gosdn_rbac_rbac_proto_rawDescData = protoimpl.X.CompressGZIP(file_gosdn_rbac_rbac_proto_rawDescData)
+	})
+	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_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
+}
+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
+}
+
+func init() { file_gosdn_rbac_rbac_proto_init() }
+func file_gosdn_rbac_rbac_proto_init() {
+	if File_gosdn_rbac_rbac_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_gosdn_rbac_rbac_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LoginRequest); 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[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LoginResponse); 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[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LogoutRequest); 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[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LogoutResponse); 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[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CreateUsersRequest); 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[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CreateUsersResponse); 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[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetUsersRequest); 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[7].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[8].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[9].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[10].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[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeleteUsersResponse); 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,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_gosdn_rbac_rbac_proto_goTypes,
+		DependencyIndexes: file_gosdn_rbac_rbac_proto_depIdxs,
+		EnumInfos:         file_gosdn_rbac_rbac_proto_enumTypes,
+		MessageInfos:      file_gosdn_rbac_rbac_proto_msgTypes,
+	}.Build()
+	File_gosdn_rbac_rbac_proto = out.File
+	file_gosdn_rbac_rbac_proto_rawDesc = nil
+	file_gosdn_rbac_rbac_proto_goTypes = nil
+	file_gosdn_rbac_rbac_proto_depIdxs = nil
+}
diff --git a/api/go/gosdn/rbac/rbac.pb.gw.go b/api/go/gosdn/rbac/rbac.pb.gw.go
new file mode 100644
index 0000000000000000000000000000000000000000..23cff1f998c166d7471cc0c9b040a2139587e290
--- /dev/null
+++ b/api/go/gosdn/rbac/rbac.pb.gw.go
@@ -0,0 +1,612 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: gosdn/rbac/rbac.proto
+
+/*
+Package rbac is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package rbac
+
+import (
+	"context"
+	"io"
+	"net/http"
+
+	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/proto"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = metadata.Join
+
+func request_AuthService_Login_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq LoginRequest
+	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.Login(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_Login_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq LoginRequest
+	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.Login(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_Logout_0 = &utilities.DoubleArray{Encoding: map[string]int{"username": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
+)
+
+func request_AuthService_Logout_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq LogoutRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["username"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username")
+	}
+
+	protoReq.Username, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_Logout_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.Logout(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_Logout_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq LogoutRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["username"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username")
+	}
+
+	protoReq.Username, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AuthService_Logout_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.Logout(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_AuthService_CreateUsers_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq CreateUsersRequest
+	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.CreateUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_CreateUsers_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq CreateUsersRequest
+	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.CreateUsers(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_GetUsers_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_GetUsers_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetUsersRequest
+	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_GetUsers_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.GetUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_GetUsers_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetUsersRequest
+	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_GetUsers_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.GetUsers(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_AuthService_UpdateUsers_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq UpdateUsersRequest
+	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.UpdateUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_UpdateUsers_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq UpdateUsersRequest
+	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.UpdateUsers(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_AuthService_DeleteUsers_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_AuthService_DeleteUsers_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq DeleteUsersRequest
+	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_DeleteUsers_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.DeleteUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_AuthService_DeleteUsers_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq DeleteUsersRequest
+	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_DeleteUsers_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.DeleteUsers(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.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterAuthServiceHandlerFromEndpoint instead.
+func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthServiceServer) error {
+
+	mux.Handle("POST", pattern_AuthService_Login_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/Login", runtime.WithHTTPPathPattern("/login"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_Login_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_Login_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_Logout_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/Logout", runtime.WithHTTPPathPattern("/logout/{username}"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_Logout_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_Logout_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_CreateUsers_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/CreateUsers", runtime.WithHTTPPathPattern("/users/create"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_CreateUsers_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_CreateUsers_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()
+		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/GetUsers", runtime.WithHTTPPathPattern("/users"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_GetUsers_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_GetUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_UpdateUsers_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/UpdateUsers", runtime.WithHTTPPathPattern("/users/update"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_UpdateUsers_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_UpdateUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("DELETE", pattern_AuthService_DeleteUsers_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/DeleteUsers", runtime.WithHTTPPathPattern("/users/delete"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_AuthService_DeleteUsers_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_DeleteUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+// RegisterAuthServiceHandlerFromEndpoint is same as RegisterAuthServiceHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterAuthServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+	conn, err := grpc.Dial(endpoint, opts...)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err != nil {
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+			return
+		}
+		go func() {
+			<-ctx.Done()
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+		}()
+	}()
+
+	return RegisterAuthServiceHandler(ctx, mux, conn)
+}
+
+// RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+	return RegisterAuthServiceHandlerClient(ctx, mux, NewAuthServiceClient(conn))
+}
+
+// RegisterAuthServiceHandlerClient registers the http handlers for service AuthService
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AuthServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "AuthServiceClient" to call the correct interceptors.
+func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthServiceClient) error {
+
+	mux.Handle("POST", pattern_AuthService_Login_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/Login", runtime.WithHTTPPathPattern("/login"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_Login_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_Login_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_Logout_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/Logout", runtime.WithHTTPPathPattern("/logout/{username}"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_Logout_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_Logout_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_CreateUsers_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/CreateUsers", runtime.WithHTTPPathPattern("/users/create"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_CreateUsers_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_CreateUsers_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()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gosdn.rbac.AuthService/GetUsers", runtime.WithHTTPPathPattern("/users"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_GetUsers_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_GetUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_AuthService_UpdateUsers_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/UpdateUsers", runtime.WithHTTPPathPattern("/users/update"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_UpdateUsers_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_UpdateUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("DELETE", pattern_AuthService_DeleteUsers_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/DeleteUsers", runtime.WithHTTPPathPattern("/users/delete"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_AuthService_DeleteUsers_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_DeleteUsers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+var (
+	pattern_AuthService_Login_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"login"}, ""))
+
+	pattern_AuthService_Logout_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"logout", "username"}, ""))
+
+	pattern_AuthService_CreateUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"users", "create"}, ""))
+
+	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"}, ""))
+)
+
+var (
+	forward_AuthService_Login_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_Logout_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_CreateUsers_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_GetUsers_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_UpdateUsers_0 = runtime.ForwardResponseMessage
+
+	forward_AuthService_DeleteUsers_0 = runtime.ForwardResponseMessage
+)
diff --git a/api/go/gosdn/rbac/rbac_grpc.pb.go b/api/go/gosdn/rbac/rbac_grpc.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..781a8fd9b64fe92b2c468604a83daec3376e966f
--- /dev/null
+++ b/api/go/gosdn/rbac/rbac_grpc.pb.go
@@ -0,0 +1,305 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+
+package rbac
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// AuthServiceClient is the client API for AuthService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type AuthServiceClient interface {
+	// Allows a user to login creating a session for further actions.
+	Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
+	// Allows a user to log out from an existing session.
+	Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error)
+	// Create users with the provided parameters, creation of multiple users requires login beforehand.
+	// 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 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.
+	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)
+}
+
+type authServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
+	return &authServiceClient{cc}
+}
+
+func (c *authServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
+	out := new(LoginResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/Login", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error) {
+	out := new(LogoutResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/Logout", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) CreateUsers(ctx context.Context, in *CreateUsersRequest, opts ...grpc.CallOption) (*CreateUsersResponse, error) {
+	out := new(CreateUsersResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/CreateUsers", 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...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) UpdateUsers(ctx context.Context, in *UpdateUsersRequest, opts ...grpc.CallOption) (*UpdateUsersResponse, error) {
+	out := new(UpdateUsersResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/UpdateUsers", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *authServiceClient) DeleteUsers(ctx context.Context, in *DeleteUsersRequest, opts ...grpc.CallOption) (*DeleteUsersResponse, error) {
+	out := new(DeleteUsersResponse)
+	err := c.cc.Invoke(ctx, "/gosdn.rbac.AuthService/DeleteUsers", 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
+type AuthServiceServer interface {
+	// Allows a user to login creating a session for further actions.
+	Login(context.Context, *LoginRequest) (*LoginResponse, error)
+	// Allows a user to log out from an existing session.
+	Logout(context.Context, *LogoutRequest) (*LogoutResponse, error)
+	// Create users with the provided parameters, creation of multiple users requires login beforehand.
+	// 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 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.
+	UpdateUsers(context.Context, *UpdateUsersRequest) (*UpdateUsersResponse, error)
+	// Deletes users, requires login beforehand.
+	// Requires highest permissions.
+	DeleteUsers(context.Context, *DeleteUsersRequest) (*DeleteUsersResponse, error)
+	mustEmbedUnimplementedAuthServiceServer()
+}
+
+// UnimplementedAuthServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedAuthServiceServer struct {
+}
+
+func (UnimplementedAuthServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
+}
+func (UnimplementedAuthServiceServer) Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
+}
+func (UnimplementedAuthServiceServer) CreateUsers(context.Context, *CreateUsersRequest) (*CreateUsersResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CreateUsers not implemented")
+}
+func (UnimplementedAuthServiceServer) GetUsers(context.Context, *GetUsersRequest) (*GetUsersResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetUsers not implemented")
+}
+func (UnimplementedAuthServiceServer) UpdateUsers(context.Context, *UpdateUsersRequest) (*UpdateUsersResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method UpdateUsers not implemented")
+}
+func (UnimplementedAuthServiceServer) DeleteUsers(context.Context, *DeleteUsersRequest) (*DeleteUsersResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DeleteUsers not implemented")
+}
+func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
+
+// UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to AuthServiceServer will
+// result in compilation errors.
+type UnsafeAuthServiceServer interface {
+	mustEmbedUnimplementedAuthServiceServer()
+}
+
+func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
+	s.RegisterService(&AuthService_ServiceDesc, srv)
+}
+
+func _AuthService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(LoginRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).Login(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/Login",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).Login(ctx, req.(*LoginRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_Logout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(LogoutRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).Logout(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/Logout",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).Logout(ctx, req.(*LogoutRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_CreateUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(CreateUsersRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).CreateUsers(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/CreateUsers",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).CreateUsers(ctx, req.(*CreateUsersRequest))
+	}
+	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 {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).GetUsers(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/GetUsers",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).GetUsers(ctx, req.(*GetUsersRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_UpdateUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(UpdateUsersRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).UpdateUsers(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/UpdateUsers",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).UpdateUsers(ctx, req.(*UpdateUsersRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AuthService_DeleteUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(DeleteUsersRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AuthServiceServer).DeleteUsers(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/gosdn.rbac.AuthService/DeleteUsers",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AuthServiceServer).DeleteUsers(ctx, req.(*DeleteUsersRequest))
+	}
+	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)
+var AuthService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "gosdn.rbac.AuthService",
+	HandlerType: (*AuthServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Login",
+			Handler:    _AuthService_Login_Handler,
+		},
+		{
+			MethodName: "Logout",
+			Handler:    _AuthService_Logout_Handler,
+		},
+		{
+			MethodName: "CreateUsers",
+			Handler:    _AuthService_CreateUsers_Handler,
+		},
+		{
+			MethodName: "GetUsers",
+			Handler:    _AuthService_GetUsers_Handler,
+		},
+		{
+			MethodName: "UpdateUsers",
+			Handler:    _AuthService_UpdateUsers_Handler,
+		},
+		{
+			MethodName: "DeleteUsers",
+			Handler:    _AuthService_DeleteUsers_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 a2169e773e7a0ee3b2845ea9045f48b288852d4e..d7787740e84767031e74059eb15666340add3b6c 100644
--- a/api/openapiv2/gosdn_northbound.swagger.json
+++ b/api/openapiv2/gosdn_northbound.swagger.json
@@ -20,9 +20,6 @@
     {
       "name": "Collector"
     },
-    {
-      "name": "SbiService"
-    },
     {
       "name": "gNMI"
     },
@@ -30,10 +27,16 @@
       "name": "AgentManager"
     },
     {
-      "name": "CsbiService"
+      "name": "SbiService"
     },
     {
       "name": "CoreService"
+    },
+    {
+      "name": "CsbiService"
+    },
+    {
+      "name": "AuthService"
     }
   ],
   "consumes": [
@@ -43,6 +46,77 @@
     "application/json"
   ],
   "paths": {
+    "/login": {
+      "post": {
+        "summary": "Allows a user to login creating a session for further actions.",
+        "operationId": "AuthService_Login",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacLoginResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/rbacLoginRequest"
+            }
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/logout/{username}": {
+      "post": {
+        "summary": "Allows a user to log out from an existing session.",
+        "operationId": "AuthService_Logout",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacLogoutResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "username",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
     "/pnd/{pid}": {
       "get": {
         "summary": "Allows to request a specific Principal Network Domain.",
@@ -734,6 +808,152 @@
           "PndService"
         ]
       }
+    },
+    "/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"
+          },
+          {
+            "name": "token",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          }
+        ],
+        "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.",
+        "operationId": "AuthService_CreateUsers",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacCreateUsersResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/rbacCreateUsersRequest"
+            }
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
+    },
+    "/users/delete": {
+      "delete": {
+        "summary": "Deletes users, requires login beforehand.\nRequires highest permissions.",
+        "operationId": "AuthService_DeleteUsers",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacDeleteUsersResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "timestamp",
+            "in": "query",
+            "required": false,
+            "type": "string",
+            "format": "int64"
+          },
+          {
+            "name": "username",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "multi"
+          }
+        ],
+        "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.",
+        "operationId": "AuthService_UpdateUsers",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/rbacUpdateUsersResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/rbacUpdateUsersRequest"
+            }
+          }
+        ],
+        "tags": [
+          "AuthService"
+        ]
+      }
     }
   },
   "definitions": {
@@ -2009,6 +2229,15 @@
       ],
       "default": "STATUS_UNSPECIFIED"
     },
+    "gosdnrbacStatus": {
+      "type": "string",
+      "enum": [
+        "STATUS_UNSPECIFIED",
+        "STATUS_OK",
+        "STATUS_ERROR"
+      ],
+      "default": "STATUS_UNSPECIFIED"
+    },
     "gosdnsouthboundPayload": {
       "type": "object",
       "properties": {
@@ -2391,6 +2620,145 @@
       "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    }"
     },
+    "rbacCreateUsersRequest": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "user": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/rbacUser"
+          }
+        }
+      },
+      "title": "CreateUsers"
+    },
+    "rbacCreateUsersResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
+    "rbacDeleteUsersResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
+    "rbacGetUsersResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        },
+        "user": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/rbacUser"
+          }
+        }
+      }
+    },
+    "rbacLoginRequest": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "username": {
+          "type": "string"
+        },
+        "pwd": {
+          "type": "string"
+        }
+      },
+      "title": "Login"
+    },
+    "rbacLoginResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        },
+        "token": {
+          "type": "string"
+        }
+      }
+    },
+    "rbacLogoutResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
+    "rbacUpdateUsersRequest": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "user": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/rbacUser"
+          }
+        }
+      },
+      "title": "UpdateUsers"
+    },
+    "rbacUpdateUsersResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/gosdnrbacStatus"
+        }
+      }
+    },
+    "rbacUser": {
+      "type": "string",
+      "enum": [
+        "USER_UNSPECIFIED",
+        "USER_NAME",
+        "USER_PWD"
+      ],
+      "default": "USER_UNSPECIFIED",
+      "title": "TODO: add additional data to user enum"
+    },
     "southboundSouthboundInterface": {
       "type": "object",
       "properties": {
diff --git a/api/proto/buf.lock b/api/proto/buf.lock
index 1af83a2967b4a3cd305b0b03c52188a6f85567a7..18424600585920365ca34c2abca5d6ade0947705 100644
--- a/api/proto/buf.lock
+++ b/api/proto/buf.lock
@@ -4,14 +4,8 @@ deps:
   - remote: buf.build
     owner: googleapis
     repository: googleapis
-    branch: main
-    commit: 2de8fdf478b545239f089219bf532912
-    digest: b1-8pfrYEi1AMHlTqsEo1vcBZQ0DwB8PYQdmYGm8m3PRr0=
-    create_time: 2022-03-22T15:09:37.061763Z
+    commit: 0f45818f23164927b753ca450a5ed5bf
   - remote: buf.build
     owner: grpc-ecosystem
     repository: grpc-gateway
-    branch: main
     commit: f85c60ac38544f2d8f346491c9d916e5
-    digest: b1-qqV9hYsmYDGNXfic_Vap4lKVarQS4-RGAllaxwnTljM=
-    create_time: 2022-03-21T20:28:59.8282Z
diff --git a/api/proto/gosdn/rbac/rbac.proto b/api/proto/gosdn/rbac/rbac.proto
new file mode 100644
index 0000000000000000000000000000000000000000..9145b8d64c8950d723b73c897b72e8f500fa6587
--- /dev/null
+++ b/api/proto/gosdn/rbac/rbac.proto
@@ -0,0 +1,147 @@
+syntax = "proto3";
+
+package gosdn.rbac;
+
+import "google/api/annotations.proto";
+import "google/protobuf/descriptor.proto";
+import "protoc-gen-openapiv2/options/annotations.proto";
+
+option go_package = "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac";
+
+service AuthService {
+
+    // Allows a user to login creating a session for further actions.
+    rpc Login(LoginRequest) returns (LoginResponse) {
+        option (google.api.http) = {
+            post: "/login"
+            body: "*"
+        };
+    }
+
+    // Allows a user to log out from an existing session.
+    rpc Logout(LogoutRequest) returns (LogoutResponse) {
+        option (google.api.http) = {
+            post: "/logout/{username}"
+        };
+    }
+
+    // Create users with the provided parameters, creation of multiple users requires login beforehand.
+    // 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.
+    rpc CreateUsers(CreateUsersRequest) returns (CreateUsersResponse) {
+        option (google.api.http) = {
+            post: "/users/create"
+            body: "*"
+        };
+    }
+
+    // Requests information about available users, requires login beforehand.
+    // Requires highest possible permissions.
+    rpc GetUsers (GetUsersRequest) returns (GetUsersResponse) {
+        option (google.api.http) = {
+            get: "/users"
+        };
+    }
+
+    // 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.
+    rpc UpdateUsers (UpdateUsersRequest) returns (UpdateUsersResponse) {
+        option (google.api.http) = {
+            post: "/users/update"
+            body: "*"
+        };
+    }
+
+    // Deletes users, requires login beforehand.
+    // Requires highest permissions.
+    rpc DeleteUsers(DeleteUsersRequest) returns (DeleteUsersResponse) {
+        option (google.api.http) = {
+            delete: "/users/delete"
+        };
+    }
+
+}
+
+enum Status {
+    STATUS_UNSPECIFIED = 0;
+    STATUS_OK = 1;
+    STATUS_ERROR = 2;
+}
+
+// TODO: add additional data to user enum
+enum User {
+    USER_UNSPECIFIED = 0;
+    USER_NAME = 1;
+    USER_PWD = 2;
+    // ...
+}
+
+// Login
+message LoginRequest {
+    int64 timestamp = 1;
+    string username = 2;
+    string pwd = 3;
+}
+
+message LoginResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+    string token = 3;
+}
+
+// Logout
+message LogoutRequest {
+    int64 timestamp = 1;
+    string username = 2;
+}
+
+message LogoutResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
+
+// CreateUsers
+message CreateUsersRequest {
+    int64 timestamp = 1;
+    repeated User user = 2;
+}
+
+message CreateUsersResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
+
+// GetUsers
+message GetUsersRequest {
+    int64 timestamp = 1;
+    string token = 2;
+}
+
+message GetUsersResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+    repeated User user = 3;
+}
+
+// UpdateUsers
+message UpdateUsersRequest {
+    int64 timestamp = 1;
+    repeated User user = 2;
+}
+
+message UpdateUsersResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
+
+// DeleteUsers
+message DeleteUsersRequest {
+    int64 timestamp = 1;
+    repeated string username = 2;
+}
+
+message DeleteUsersResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+}
diff --git a/cli/cmd/login.go b/cli/cmd/login.go
index fd72fc6ea74f66d5a72ad80d74b18c820a15dac8..d8a1891ecb03e9bdc1b328e73cec10328f575e58 100644
--- a/cli/cmd/login.go
+++ b/cli/cmd/login.go
@@ -32,22 +32,35 @@ POSSIBILITY OF SUCH DAMAGE.
 package cmd
 
 import (
-	"fmt"
-
+	"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 loginCmd = &cobra.Command{
 	Use:   "login",
-	Short: "TODO - not yet implemented",
-	Long:  `TODO - not yet implemented`,
+	Short: "Logs in for further actions",
+	Long: `Logs the user in to allow further actions on the controller.
+    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)
+		if err != nil {
+			return err
+		}
 
-	Run: func(cmd *cobra.Command, args []string) {
-		fmt.Println("login called")
+		// TODO: set token here
+		log.Info("LoginResponse: " + resp.Token)
+
+		return nil
 	},
 }
 
+var nbUser string
+var nbUserPwd string
+
 func init() {
 	rootCmd.AddCommand(loginCmd)
 
@@ -60,4 +73,6 @@ func init() {
 	// 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(&nbUserPwd, "p", "", "pwd for login")
 }
diff --git a/cli/cmd/logout.go b/cli/cmd/logout.go
new file mode 100644
index 0000000000000000000000000000000000000000..3fd3ac068767a1e498e7b8ac1a2aba64f04eadf4
--- /dev/null
+++ b/cli/cmd/logout.go
@@ -0,0 +1,73 @@
+/*
+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"
+)
+
+// logoutCmd represents the logout command
+var logoutCmd = &cobra.Command{
+	Use:   "logout",
+	Short: "Logs the current user out",
+	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)
+		if err != nil {
+			return err
+		}
+
+		// TODO: unset session proof here
+		log.Info("LogoutResponse: " + resp.Status.String())
+
+		return nil
+	},
+}
+
+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")
+}
diff --git a/controller/api/grpc.go b/controller/api/grpc.go
index f416a259773eac0dc8b0a289713db006793ca7d6..0ec442391a2e3876c3c16d6c802e87f412811ca4 100644
--- a/controller/api/grpc.go
+++ b/controller/api/grpc.go
@@ -8,6 +8,7 @@ import (
 
 	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
 	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
 	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
 	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
 	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
@@ -402,3 +403,30 @@ func SendChangeRequest(addr, pid string, req *ppb.ChangeRequest) (*ppb.SetPathLi
 	}
 	return pndClient.SetPathList(ctx, r)
 }
+
+func Login(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,
+		Pwd:       pwd,
+	}
+	return authClient.Login(ctx, r)
+}
+
+func Logout(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)
+}
diff --git a/controller/controller.go b/controller/controller.go
index e56e35c729b5351d83c9db3076f5867ae8020a57..4281991cb314cd0386bb954ca1af14992759c943 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -18,9 +18,11 @@ import (
 	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
 	cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
 	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
 	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
 	"code.fbi.h-da.de/danet/gosdn/controller/config"
 	"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/store"
 
@@ -93,12 +95,14 @@ func startGrpc() error {
 		return err
 	}
 	log.Infof("listening to %v", lis.Addr())
-	c.grpcServer = grpc.NewServer()
+
+	c.grpcServer = grpc.NewServer(grpc.UnaryInterceptor(server.AuthInterceptor{}.Unary()))
 	c.nbi = nbi.NewNBI(c.pndc)
 	pb.RegisterCoreServiceServer(c.grpcServer, c.nbi.Core)
 	ppb.RegisterPndServiceServer(c.grpcServer, c.nbi.Pnd)
 	cpb.RegisterCsbiServiceServer(c.grpcServer, c.nbi.Csbi)
 	spb.RegisterSbiServiceServer(c.grpcServer, c.nbi.Sbi)
+	apb.RegisterAuthServiceServer(c.grpcServer, c.nbi.Auth)
 	go func() {
 		if err := c.grpcServer.Serve(lis); err != nil {
 			log.Fatal(err)
diff --git a/controller/http.go b/controller/http.go
index e119e035538f81fed07a6a271bdc657ccd26ddf4..041163c5e98d00c4829c38466ce57c48c36744ed 100644
--- a/controller/http.go
+++ b/controller/http.go
@@ -17,6 +17,7 @@ import (
 
 	cgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
 	pgw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
+	agw "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
 )
 
 var (
@@ -59,6 +60,11 @@ func run() error {
 		return err
 	}
 
+	err = agw.RegisterAuthServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
+	if err != nil {
+		return err
+	}
+
 	// Set the HTTP server of core to the new server
 	c.httpServer = &http.Server{Addr: ":8080", Handler: mux}
 	// Start HTTP server (and proxy calls to gRPC server endpoint)
diff --git a/controller/northbound/client/rbac.go b/controller/northbound/client/rbac.go
new file mode 100644
index 0000000000000000000000000000000000000000..0d0e0889916e87094c76dbc93338ddf339ac45c1
--- /dev/null
+++ b/controller/northbound/client/rbac.go
@@ -0,0 +1,17 @@
+package client
+
+import (
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
+	"google.golang.org/grpc"
+)
+
+// AuthClient returns a client for the gRPC Auth service. It takes
+// the address of the gRPC endpoint and optional grpc.DialOption
+// as argument
+func AuthClient(addr string, opts ...grpc.DialOption) (apb.AuthServiceClient, error) {
+	conn, err := grpc.Dial(addr, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return apb.NewAuthServiceClient(conn), nil
+}
diff --git a/controller/northbound/server/auth_interceptor.go b/controller/northbound/server/auth_interceptor.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6567278562ed6a5719dae38de797e6485a17fff
--- /dev/null
+++ b/controller/northbound/server/auth_interceptor.go
@@ -0,0 +1,21 @@
+package server
+
+import (
+	"context"
+
+	log "github.com/sirupsen/logrus"
+	"google.golang.org/grpc"
+)
+
+type AuthInterceptor struct {
+}
+
+func (auth AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
+	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
+		log.Info("Interceptor called")
+
+		// TODO: Implement proper auth logic here
+
+		return handler(ctx, req)
+	}
+}
diff --git a/controller/northbound/server/nbi.go b/controller/northbound/server/nbi.go
index 50a790171f2746b150cc90be526dc2b15da46138..d88b4c5d0e2149c858d7da0b21b5abdc24f31763 100644
--- a/controller/northbound/server/nbi.go
+++ b/controller/northbound/server/nbi.go
@@ -18,6 +18,7 @@ type NorthboundInterface struct {
 	Core *core
 	Csbi *csbi
 	Sbi  *sbiServer
+	Auth *rbac
 }
 
 // NewNBI receives a PndStore and returns a new gRPC *NorthboundInterface
@@ -28,6 +29,7 @@ func NewNBI(pnds *store.PndStore) *NorthboundInterface {
 		Core: &core{},
 		Csbi: &csbi{},
 		Sbi:  &sbiServer{},
+		Auth: &rbac{},
 	}
 }
 
diff --git a/controller/northbound/server/rbac.go b/controller/northbound/server/rbac.go
new file mode 100644
index 0000000000000000000000000000000000000000..1cad8a40cb4dacdf060b5b21498c385862ac37b7
--- /dev/null
+++ b/controller/northbound/server/rbac.go
@@ -0,0 +1,94 @@
+package server
+
+import (
+	"context"
+	"time"
+
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
+	"code.fbi.h-da.de/danet/gosdn/controller/metrics"
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+type rbac struct {
+	apb.UnimplementedAuthServiceServer
+}
+
+func (r rbac) Login(ctx context.Context, request *apb.LoginRequest) (*apb.LoginResponse, error) {
+	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement proper login
+
+	return &apb.LoginResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+		Token:     "Logged in", // ADD PROPER TOKEN HERE
+	}, nil
+}
+
+func (r rbac) Logout(ctx context.Context, request *apb.LogoutRequest) (*apb.LogoutResponse, error) {
+	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement proper logout
+
+	return &apb.LogoutResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
+
+func (r rbac) CreateUsers(ctx context.Context, request *apb.CreateUsersRequest) (*apb.CreateUsersResponse, error) {
+	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement proper user creation
+
+	return &apb.CreateUsersResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
+
+func (r rbac) GetUsers(ctx context.Context, request *apb.GetUsersRequest) (*apb.GetUsersResponse, error) {
+	labels := prometheus.Labels{"service": "core", "rpc": "get"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement proper user fetching
+
+	return &apb.GetUsersResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+		// User here, probably needs fixing in proto!
+	}, nil
+}
+
+func (r rbac) UpdateUsers(ctx context.Context, request *apb.UpdateUsersRequest) (*apb.UpdateUsersResponse, error) {
+	labels := prometheus.Labels{"service": "core", "rpc": "post"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement proper user updating here
+
+	return &apb.UpdateUsersResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
+
+func (r rbac) DeleteUsers(ctx context.Context, request *apb.DeleteUsersRequest) (*apb.DeleteUsersResponse, error) {
+	labels := prometheus.Labels{"service": "core", "rpc": "delete"}
+	start := metrics.StartHook(labels, grpcRequestsTotal)
+	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
+
+	// TODO: implement proper user deletion
+
+	return &apb.DeleteUsersResponse{
+		Timestamp: time.Now().UnixNano(),
+		Status:    apb.Status_STATUS_OK,
+	}, nil
+}
diff --git a/controller/northbound/server/rbac_test.go b/controller/northbound/server/rbac_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..de40b5af2eb97d702e4c39c39c4e60486c7f15b0
--- /dev/null
+++ b/controller/northbound/server/rbac_test.go
@@ -0,0 +1,220 @@
+package server
+
+import (
+	"context"
+	"reflect"
+	"testing"
+
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
+)
+
+func Test_rbac_Login(t *testing.T) {
+	type fields struct {
+		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+	}
+	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",
+			want: "Logged in",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := rbac{
+				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			}
+			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)
+				return
+			}
+
+			got := resp.GetToken()
+
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("rbac.Login() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_rbac_Logout(t *testing.T) {
+	type fields struct {
+		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+	}
+	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.
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := rbac{
+				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			}
+			got, err := r.Logout(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("rbac.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)
+			}
+		})
+	}
+}
+
+func Test_rbac_CreateUsers(t *testing.T) {
+	type fields struct {
+		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+	}
+	type args struct {
+		ctx     context.Context
+		request *apb.CreateUsersRequest
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    *apb.CreateUsersResponse
+		wantErr bool
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := rbac{
+				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			}
+			got, err := r.CreateUsers(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("rbac.CreateUsers() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("rbac.CreateUsers() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_rbac_GetUsers(t *testing.T) {
+	type fields struct {
+		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+	}
+	type args struct {
+		ctx     context.Context
+		request *apb.GetUsersRequest
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    *apb.GetUsersResponse
+		wantErr bool
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := rbac{
+				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			}
+			got, err := r.GetUsers(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("rbac.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)
+			}
+		})
+	}
+}
+
+func Test_rbac_UpdateUsers(t *testing.T) {
+	type fields struct {
+		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+	}
+	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.
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := rbac{
+				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			}
+			got, err := r.UpdateUsers(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("rbac.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)
+			}
+		})
+	}
+}
+
+func Test_rbac_DeleteUsers(t *testing.T) {
+	type fields struct {
+		UnimplementedAuthServiceServer apb.UnimplementedAuthServiceServer
+	}
+	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.
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := rbac{
+				UnimplementedAuthServiceServer: tt.fields.UnimplementedAuthServiceServer,
+			}
+			got, err := r.DeleteUsers(tt.args.ctx, tt.args.request)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("rbac.DeleteUsers() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("rbac.DeleteUsers() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}