Skip to content

Commit da1bdc3

Browse files
committed
require admin access for force operations
1 parent 7fc48b8 commit da1bdc3

File tree

4 files changed

+103
-48
lines changed

4 files changed

+103
-48
lines changed

ydb/core/viewer/json_pdisk_restart.h

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ class TJsonPDiskRestart : public TViewerPipeClient<TJsonPDiskRestart> {
7373
0, NMon::IEvHttpInfoRes::EContentType::Custom));
7474
return PassAway();
7575
}
76+
if (Force && !Viewer->CheckAccessAdministration(Event->Get())) {
77+
TBase::Send(Event->Sender, new NMon::TEvHttpInfoRes(Viewer->GetHTTPFORBIDDEN(Event->Get()), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
78+
return PassAway();
79+
}
7680

7781
if (!NodeId) {
7882
NodeId = TlsActivationContext->ActorSystem()->NodeId;
@@ -136,35 +140,18 @@ class TJsonPDiskRestart : public TViewerPipeClient<TJsonPDiskRestart> {
136140
TBase::PassAway();
137141
}
138142

139-
void TryToTranslateFromBSC2Human(TString& bscError, bool& forceRetryPossible) {
140-
if (IsMatchesWildcard(bscError, "GroupId# * ExpectedStatus# *")) {
141-
TStringBuf groupId = TStringBuf(bscError).After(' ').Before(' ');
142-
TStringBuf expectedStatus = TStringBuf(bscError).After('#').After('#').After(' ');
143-
if (expectedStatus == "DEGRADED") {
144-
bscError = TStringBuilder() << "Calling this operation will cause at least group " << groupId << " to go into a degraded state";
145-
forceRetryPossible = true;
146-
return;
147-
}
148-
if (expectedStatus == "DISINTEGRATED") {
149-
bscError = TStringBuilder() << "Calling this operation will cause at least group " << groupId << " to go into a dead state";
150-
return;
151-
}
152-
}
153-
forceRetryPossible = false;
154-
}
155-
156143
void ReplyAndPassAway() {
157144
NJson::TJsonValue json;
158145
if (Response != nullptr) {
159146
if (Response->Record.GetResponse().GetSuccess()) {
160147
json["result"] = true;
161148
} else {
162149
json["result"] = false;
163-
TString error = Response->Record.GetResponse().GetErrorDescription();
150+
TString error;
164151
bool forceRetryPossible = false;
165-
TryToTranslateFromBSC2Human(error, forceRetryPossible);
152+
Viewer->TranslateFromBSC2Human(Response->Record.GetResponse(), error, forceRetryPossible);
166153
json["error"] = error;
167-
if (forceRetryPossible) {
154+
if (forceRetryPossible && Viewer->CheckAccessAdministration(Event->Get())) {
168155
json["forceRetryPossible"] = true;
169156
}
170157
}

ydb/core/viewer/json_vdisk_evict.h

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ class TJsonVDiskEvict : public TViewerPipeClient<TJsonVDiskEvict> {
108108
Retries = FromStringWithDefault<ui32>(params.Get("retries"), 0);
109109
RetryPeriod = TDuration::MilliSeconds(FromStringWithDefault<ui32>(params.Get("retry_period"), RetryPeriod.MilliSeconds()));
110110

111+
if (Force && !Viewer->CheckAccessAdministration(Event->Get())) {
112+
TBase::Send(Event->Sender, new NMon::TEvHttpInfoRes(Viewer->GetHTTPFORBIDDEN(Event->Get()), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
113+
return PassAway();
114+
}
115+
111116
SendRequest();
112117

113118
TBase::Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
@@ -161,43 +166,18 @@ class TJsonVDiskEvict : public TViewerPipeClient<TJsonVDiskEvict> {
161166
TBase::PassAway();
162167
}
163168

164-
void TryToTranslateFromBSC2Human(const NKikimrBlobStorage::TConfigResponse& response, TString& bscError, bool& forceRetryPossible) {
165-
if (response.GroupsGetDisintegratedByExpectedStatusSize()) {
166-
bscError = TStringBuilder() << "Calling this operation will cause at least groups [" << JoinStrings(response.GetGroupsGetDisintegratedByExpectedStatus().begin(), response.GetGroupsGetDisintegratedByExpectedStatus().end(), ", ") << "] to go into a dead state";
167-
} else if (response.GroupsGetDisintegratedSize()) {
168-
bscError = TStringBuilder() << "Calling this operation will cause at least groups [" << JoinStrings(response.GetGroupsGetDisintegrated().begin(), response.GetGroupsGetDisintegrated().end(), ", ") << "] to go into a dead state";
169-
} else if (response.GroupsGetDegradedSize()) {
170-
bscError = TStringBuilder() << "Calling this operation will cause at least groups [" << JoinStrings(response.GetGroupsGetDegraded().begin(), response.GetGroupsGetDegraded().end(), ", ") << "] to go into a degraded state";
171-
forceRetryPossible = true;
172-
} else if (response.StatusSize()) {
173-
const auto& lastStatus = response.GetStatus(response.StatusSize() - 1);
174-
TVector<ui32> groups;
175-
for (auto& failParam: lastStatus.GetFailParam()) {
176-
if (failParam.HasGroupId()) {
177-
groups.emplace_back(failParam.GetGroupId());
178-
}
179-
}
180-
if (lastStatus.GetFailReason() == NKikimrBlobStorage::TConfigResponse::TStatus::kMayGetDegraded) {
181-
bscError = TStringBuilder() << "Calling this operation will cause at least groups [" << JoinVectorIntoString(groups, ", ") << "] to go into a degraded state";
182-
forceRetryPossible = true;
183-
} else if (lastStatus.GetFailReason() == NKikimrBlobStorage::TConfigResponse::TStatus::kMayLoseData) {
184-
bscError = TStringBuilder() << "Calling this operation may result in data loss for at least groups [" << JoinVectorIntoString(groups, ", ") << "]";
185-
}
186-
}
187-
}
188-
189169
void ReplyAndPassAway() {
190170
NJson::TJsonValue json;
191171
if (Response != nullptr) {
192172
if (Response->Record.GetResponse().GetSuccess()) {
193173
json["result"] = true;
194174
} else {
195175
json["result"] = false;
196-
TString error = Response->Record.GetResponse().GetErrorDescription();
176+
TString error;
197177
bool forceRetryPossible = false;
198-
TryToTranslateFromBSC2Human(Response->Record.GetResponse(), error, forceRetryPossible);
178+
Viewer->TranslateFromBSC2Human(Response->Record.GetResponse(), error, forceRetryPossible);
199179
json["error"] = error;
200-
if (forceRetryPossible) {
180+
if (forceRetryPossible && Viewer->CheckAccessAdministration(Event->Get())) {
201181
json["forceRetryPossible"] = true;
202182
}
203183
}

ydb/core/viewer/viewer.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,92 @@ class TViewer : public TActorBootstrapped<TViewer>, public IViewer {
198198
TString GetHTTPFORBIDDEN(const NMon::TEvHttpInfo* request) override;
199199
TString GetHTTPNOTFOUND(const NMon::TEvHttpInfo* request) override;
200200

201+
bool CheckAccessAdministration(const NMon::TEvHttpInfo* request) override {
202+
if (!KikimrRunConfig.AppConfig.GetDomainsConfig().GetSecurityConfig().GetEnforceUserTokenRequirement()) {
203+
if (!KikimrRunConfig.AppConfig.GetDomainsConfig().GetSecurityConfig().GetEnforceUserTokenCheckRequirement() || request->UserToken.empty()) {
204+
return true;
205+
}
206+
}
207+
if (request->UserToken.empty()) {
208+
return false;
209+
}
210+
auto token = std::make_unique<NACLib::TUserToken>(request->UserToken);
211+
for (const auto& allowedSID : KikimrRunConfig.AppConfig.GetDomainsConfig().GetSecurityConfig().GetAdministrationAllowedSIDs()) {
212+
if (token->IsExist(allowedSID)) {
213+
return true;
214+
}
215+
}
216+
return false;
217+
}
218+
219+
static bool IsStaticGroup(ui32 groupId) {
220+
return groupId & 0x80000000 == 0;
221+
}
222+
223+
TString GetGroupList(const auto& groups) {
224+
std::vector<ui32> groupIds;
225+
for (auto group : groups) {
226+
groupIds.push_back(group);
227+
}
228+
std::sort(groupIds.begin(), groupIds.end());
229+
TStringBuilder result;
230+
if (groups.empty()) {
231+
return result << "something";
232+
}
233+
result << "at least " << groups.size();
234+
if (groups.size() > 1) {
235+
result << " groups (";
236+
} else {
237+
result << " group (";
238+
}
239+
auto was_groups = 0;
240+
auto max_groups = 3;
241+
for (auto group : groupIds) {
242+
if (was_groups > 0) {
243+
result << ", ";
244+
}
245+
if (was_groups >= max_groups) {
246+
result << "...";
247+
}
248+
if (IsStaticGroup(group)) {
249+
result << "static ";
250+
}
251+
result << group;
252+
++was_groups;
253+
}
254+
result << ")";
255+
return result;
256+
}
257+
258+
void TranslateFromBSC2Human(const NKikimrBlobStorage::TConfigResponse& response, TString& bscError, bool& forceRetryPossible) override {
259+
forceRetryPossible = false;
260+
if (response.GroupsGetDisintegratedByExpectedStatusSize()) {
261+
bscError = TStringBuilder() << "Calling this operation could cause " << GetGroupList(response.GetGroupsGetDisintegratedByExpectedStatus()) << " to go into a dead state";
262+
} else if (response.GroupsGetDisintegratedSize()) {
263+
bscError = TStringBuilder() << "Calling this operation will cause " << GetGroupList(response.GetGroupsGetDisintegrated()) << " to go into a dead state";
264+
} else if (response.GroupsGetDegradedSize()) {
265+
bscError = TStringBuilder() << "Calling this operation will cause " << GetGroupList(response.GetGroupsGetDegraded()) << " to go into a degraded state";
266+
forceRetryPossible = true;
267+
} else if (response.StatusSize()) {
268+
const auto& lastStatus = response.GetStatus(response.StatusSize() - 1);
269+
TVector<ui32> groups;
270+
for (auto& failParam: lastStatus.GetFailParam()) {
271+
if (failParam.HasGroupId()) {
272+
groups.emplace_back(failParam.GetGroupId());
273+
}
274+
}
275+
if (lastStatus.GetFailReason() == NKikimrBlobStorage::TConfigResponse::TStatus::kMayGetDegraded) {
276+
bscError = TStringBuilder() << "Calling this operation will cause " << GetGroupList(groups) << " to go into a degraded state";
277+
forceRetryPossible = true;
278+
} else if (lastStatus.GetFailReason() == NKikimrBlobStorage::TConfigResponse::TStatus::kMayLoseData) {
279+
bscError = TStringBuilder() << "Calling this operation may result in data loss for " << GetGroupList(groups);
280+
}
281+
}
282+
if (bscError.empty()) {
283+
bscError = response.GetErrorDescription();
284+
}
285+
}
286+
201287
void RegisterVirtualHandler(
202288
NKikimrViewer::EObjectType parentObjectType,
203289
TVirtualHandlerType handler) override {

ydb/core/viewer/viewer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ class IViewer {
173173
virtual TString GetHTTPBADREQUEST(const NMon::TEvHttpInfo* request, TString contentType = {}, TString response = {}) = 0;
174174
virtual TString GetHTTPFORBIDDEN(const NMon::TEvHttpInfo* request) = 0;
175175
virtual TString GetHTTPNOTFOUND(const NMon::TEvHttpInfo* request) = 0;
176+
virtual bool CheckAccessAdministration(const NMon::TEvHttpInfo* request) = 0;
177+
virtual void TranslateFromBSC2Human(const NKikimrBlobStorage::TConfigResponse& response, TString& bscError, bool& forceRetryPossible) = 0;
176178
};
177179

178180
void SetupPQVirtualHandlers(IViewer* viewer);

0 commit comments

Comments
 (0)