Skip to content

Commit

Permalink
Temp admin labels
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Zaoral <[email protected]>
  • Loading branch information
Pepo48 committed Jul 29, 2024
1 parent a56a49f commit 145565d
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3217,4 +3217,5 @@ forgotPasswordHelp=Specifies independent timeout for forgot password.
executeActionsHelp=Specifies independent timeout for execute actions.
validatingX509CertsHelp=The public certificates Keycloak uses to validate the signatures of SAML requests and responses from the external IDP when Use metadata descriptor URL is OFF. Multiple certificates can be entered separated by comma (,). The certificates can be re-imported from the Metadata descriptor URL clicking the Import Keys action in the identity provider page. The action downloads the current certificates in the metadata endpoint and assigns them to the config in this same option. You need to click Save to definitely store the re-imported certificates.
loggedInAsTempAdminUser=You are logged in as a temporary admin user. To harden security, create a permanent admin account and delete the temporary one.
temporaryAdmin=temporary admin
temporaryAdmin=Temporary admin user account. Ensure it is replaced with a permanent admin user account as soon as possible.
temporaryService=Temporary admin service account. Ensure it is replaced with a permanent admin service account as soon as possible.
10 changes: 10 additions & 0 deletions js/apps/admin-ui/src/clients/ClientsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
Tab,
TabTitleText,
ToolbarItem,
Tooltip,
} from "@patternfly/react-core";
import { WarningTriangleIcon } from "@patternfly/react-icons";
import {
IFormatter,
IFormatterValueType,
Expand Down Expand Up @@ -71,6 +73,14 @@ const ClientDetailLink = (client: ClientRepresentation) => {
</Badge>
)}
</Link>
{client.attributes?.["temporary_admin"] == "true" && (
<Tooltip content={t("temporaryService")}>
<WarningTriangleIcon
style={{ color: "gold" }}
className="pf-v5-u-ml-sm"
/>
</Tooltip>
)}
</TableText>
);
};
Expand Down
18 changes: 15 additions & 3 deletions js/apps/admin-ui/src/components/users/UserDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,23 @@ export type UserAttribute = {
};

const UserDetailLink = (user: BruteUser) => {
const { t } = useTranslation();
const { realm } = useRealm();
return (
<Link to={toUser({ realm, id: user.id!, tab: "settings" })}>
{user.username} <StatusRow user={user} />
</Link>
<>
<Link to={toUser({ realm, id: user.id!, tab: "settings" })}>
{user.username}
<StatusRow user={user} />
</Link>
{user.attributes?.["temporary_admin"] == "true" && (
<Tooltip content={t("temporaryAdmin")}>
<WarningTriangleIcon
style={{ color: "gold" }}
className="pf-v5-u-ml-sm"
/>
</Tooltip>
)}
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import org.keycloak.services.resources.admin.permissions.UserPermissionEvaluator;
import org.keycloak.utils.SearchQueryUtils;

import static org.keycloak.services.managers.ApplianceBootstrap.TEMP_ADMIN_ATTR_NAME;

public class BruteForceUsersResource {
private static final Logger logger = Logger.getLogger(BruteForceUsersResource.class);
private static final String SEARCH_ID_PARAMETER = "id:";
Expand Down Expand Up @@ -167,6 +169,12 @@ private Stream<BruteUser> toRepresentation(RealmModel realm, UserPermissionEvalu
ModelToRepresentation.toBriefRepresentation(user) :
ModelToRepresentation.toRepresentation(session, realm, user);
userRep.setAccess(usersEvaluator.getAccess(user));
if (Boolean.parseBoolean(user.getFirstAttribute(TEMP_ADMIN_ATTR_NAME))) {
if (userRep.getAttributes() == null) {
userRep.setAttributes(new HashMap<>());
}
userRep.getAttributes().put(TEMP_ADMIN_ATTR_NAME, Collections.singletonList("true"));
}
return userRep;
}).map(this::getBruteForceStatus);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,13 @@ public void createTemporaryMasterRealmAdminService(String clientId, String clien
adminClient.setSecret(clientSecret);

ClientModel adminClientModel = ClientManager.createClient(session, realm, adminClient);
adminClientModel.setAttribute(TEMP_ADMIN_ATTR_NAME, Boolean.TRUE.toString());

new ClientManager(new RealmManager(session)).enableServiceAccount(adminClientModel);
UserModel serviceAccount = session.users().getServiceAccount(adminClientModel);
RoleModel adminRole = realm.getRole(AdminRoles.ADMIN);
serviceAccount.grantRole(adminRole);

serviceAccount.setSingleAttribute(TEMP_ADMIN_ATTR_NAME, Boolean.TRUE.toString());
// also set the expiration - could be relative to a creation timestamp, or computed

ServicesLogger.LOGGER.createdTemporaryAdminService(clientId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

import static java.lang.Boolean.TRUE;
import static org.keycloak.services.managers.ApplianceBootstrap.TEMP_ADMIN_ATTR_NAME;
import static org.keycloak.utils.StreamsUtil.paginatedStream;

/**
Expand Down Expand Up @@ -144,14 +147,17 @@ public Stream<ClientRepresentation> getClients(@Parameter(description = "filter
Stream<ClientRepresentation> s = ModelToRepresentation.filterValidRepresentations(clientModels,
c -> {
ClientRepresentation representation = null;
boolean isTemporaryAdmin = Boolean.parseBoolean(c.getAttribute(TEMP_ADMIN_ATTR_NAME));
if (canView || auth.clients().canView(c)) {
representation = ModelToRepresentation.toRepresentation(c, session);
representation.setAccess(auth.clients().getAccess(c));
addTemporaryAdminAttribute(isTemporaryAdmin, representation);
} else if (!viewableOnly && auth.clients().canView(c)) {
representation = new ClientRepresentation();
representation.setId(c.getId());
representation.setClientId(c.getClientId());
representation.setDescription(c.getDescription());
addTemporaryAdminAttribute(isTemporaryAdmin, representation);
}

return representation;
Expand All @@ -164,6 +170,15 @@ public Stream<ClientRepresentation> getClients(@Parameter(description = "filter
return s;
}

private void addTemporaryAdminAttribute(boolean isTemporaryAdmin, ClientRepresentation representation) {
if (isTemporaryAdmin) {
if (representation.getAttributes() == null) {
representation.setAttributes(new HashMap<>());
}
representation.getAttributes().put(TEMP_ADMIN_ATTR_NAME, "true");
}
}

private AuthorizationService getAuthorizationService(ClientModel clientModel) {
return new AuthorizationService(session, clientModel, auth, adminEvent);
}
Expand Down

0 comments on commit 145565d

Please sign in to comment.