├── gradle.properties
├── structurizr-onpremises-plugin
├── gradle.properties
├── src
│ └── main
│ │ └── java
│ │ └── com
│ │ └── structurizr
│ │ └── onpremises
│ │ └── component
│ │ └── workspace
│ │ ├── WorkspaceEventListener.java
│ │ ├── WorkspaceEvent.java
│ │ └── WorkspaceProperties.java
└── build.gradle
├── settings.gradle
├── structurizr-onpremises
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── WEB-INF
│ │ │ │ ├── fragments
│ │ │ │ │ ├── workspace
│ │ │ │ │ │ ├── auto-save.jspf
│ │ │ │ │ │ └── auto-refresh.jspf
│ │ │ │ │ ├── dashboard-workspaces-page-control.jspf
│ │ │ │ │ ├── coda.jspf
│ │ │ │ │ └── diagrams
│ │ │ │ │ │ └── publish.jspf
│ │ │ │ ├── jboss-web.xml
│ │ │ │ ├── raw-views
│ │ │ │ │ ├── json.jsp
│ │ │ │ │ └── plaintext.jsp
│ │ │ │ ├── views
│ │ │ │ │ ├── feature-not-available.jsp
│ │ │ │ │ ├── public-dsl-editor-disabled.jsp
│ │ │ │ │ ├── home.jsp
│ │ │ │ │ ├── workspace-could-not-be-locked.jsp
│ │ │ │ │ ├── dashboard.jsp
│ │ │ │ │ ├── workspace-is-client-side-encrypted.jsp
│ │ │ │ │ ├── workspace-is-readonly.jsp
│ │ │ │ │ ├── dsl-editor-disabled.jsp
│ │ │ │ │ ├── workspace-branches-not-enabled.jsp
│ │ │ │ │ ├── user-profile.jsp
│ │ │ │ │ ├── workspace-locked.jsp
│ │ │ │ │ ├── images.jsp
│ │ │ │ │ └── reviews.jsp
│ │ │ │ ├── glassfish-web.xml
│ │ │ │ ├── applicationContext-session-local.xml
│ │ │ │ ├── applicationContext-security.xml
│ │ │ │ ├── applicationContext.xml
│ │ │ │ ├── applicationContext-security-saml.xml
│ │ │ │ ├── applicationContext-session-redis.xml
│ │ │ │ ├── applicationContext-security-ldap.xml
│ │ │ │ ├── applicationContext-security-inmemory.xml
│ │ │ │ ├── root-servlet.xml
│ │ │ │ └── applicationContext-security-file.xml
│ │ │ └── favicon.ico
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── structurizr
│ │ │ │ └── onpremises
│ │ │ │ ├── domain
│ │ │ │ ├── AuthenticationMethod.java
│ │ │ │ ├── review
│ │ │ │ │ ├── ReviewType.java
│ │ │ │ │ ├── CommentType.java
│ │ │ │ │ ├── Diagram.java
│ │ │ │ │ ├── Comment.java
│ │ │ │ │ └── Session.java
│ │ │ │ ├── MessageType.java
│ │ │ │ ├── Role.java
│ │ │ │ ├── UserType.java
│ │ │ │ ├── InputStreamAndContentLength.java
│ │ │ │ ├── Image.java
│ │ │ │ ├── Messages.java
│ │ │ │ └── Message.java
│ │ │ │ ├── configuration
│ │ │ │ ├── StructurizrEnvironmentVariables.java
│ │ │ │ ├── Configurer.java
│ │ │ │ ├── AzureBlobStorageConfigurer.java
│ │ │ │ ├── AmazonWebServicesS3Configurer.java
│ │ │ │ ├── PropertyPlaceholderConfigurer.java
│ │ │ │ ├── Features.java
│ │ │ │ ├── SamlConfigurer.java
│ │ │ │ ├── StructurizrDataDirectory.java
│ │ │ │ ├── DefaultsConfigurer.java
│ │ │ │ ├── ElasticsearchConfigurer.java
│ │ │ │ └── RedisConfigurer.java
│ │ │ │ ├── util
│ │ │ │ ├── RandomGuidGenerator.java
│ │ │ │ ├── JsonUtils.java
│ │ │ │ ├── EarlyAccessFeaturesNotAvailableException.java
│ │ │ │ ├── HtmlUtils.java
│ │ │ │ ├── WorkspaceValidationUtils.java
│ │ │ │ ├── AmazonS3ClientUtils.java
│ │ │ │ ├── Version.java
│ │ │ │ └── DateUtils.java
│ │ │ │ ├── component
│ │ │ │ ├── workspace
│ │ │ │ │ ├── WorkspaceMetadataCache.java
│ │ │ │ │ ├── WorkspaceComponentException.java
│ │ │ │ │ ├── component.xml
│ │ │ │ │ ├── NoOpWorkspaceMetadataCache.java
│ │ │ │ │ ├── WorkspaceLockResponse.java
│ │ │ │ │ ├── WorkspaceBranch.java
│ │ │ │ │ ├── WorkspaceVersion.java
│ │ │ │ │ ├── WorkspaceDao.java
│ │ │ │ │ ├── AbstractWorkspaceDao.java
│ │ │ │ │ ├── JCacheWorkspaceMetadataCache.java
│ │ │ │ │ ├── LocalWorkspaceMetadataCache.java
│ │ │ │ │ └── WorkspaceComponent.java
│ │ │ │ ├── search
│ │ │ │ │ ├── DocumentType.java
│ │ │ │ │ ├── SearchComponentException.java
│ │ │ │ │ ├── component.xml
│ │ │ │ │ ├── SearchComponent.java
│ │ │ │ │ ├── NoOpSearchComponentImpl.java
│ │ │ │ │ ├── SearchResponse.java
│ │ │ │ │ ├── Document.java
│ │ │ │ │ ├── SearchResult.java
│ │ │ │ │ ├── SearchComponentImpl.java
│ │ │ │ │ └── AbstractSearchComponentImpl.java
│ │ │ │ └── review
│ │ │ │ │ ├── ReviewException.java
│ │ │ │ │ ├── ReviewComponentException.java
│ │ │ │ │ ├── component.xml
│ │ │ │ │ ├── FileTypeAndContent.java
│ │ │ │ │ ├── ReviewComponent.java
│ │ │ │ │ └── ReviewDao.java
│ │ │ │ └── web
│ │ │ │ ├── api
│ │ │ │ ├── ApiException.java
│ │ │ │ ├── HttpHeaders.java
│ │ │ │ ├── HmacContent.java
│ │ │ │ ├── HttpUnauthorizedException.java
│ │ │ │ ├── WorkspacesApiResponse.java
│ │ │ │ ├── Md5Digest.java
│ │ │ │ ├── ApiResponse.java
│ │ │ │ ├── HashBasedMessageAuthenticationCode.java
│ │ │ │ ├── HmacAuthorizationHeader.java
│ │ │ │ └── WorkspaceApiResponse.java
│ │ │ │ ├── RawViewResolver.java
│ │ │ │ ├── NoOpSpringSessionRepositoryFilter.java
│ │ │ │ ├── error
│ │ │ │ ├── Http404Controller.java
│ │ │ │ └── Http500Controller.java
│ │ │ │ ├── security
│ │ │ │ ├── CsrfSecurityRequestMatcher.java
│ │ │ │ ├── BcryptController.java
│ │ │ │ ├── AuthenticationFailureHandler.java
│ │ │ │ ├── SignoutController.java
│ │ │ │ ├── AuthenticationSuccessHandler.java
│ │ │ │ └── SignInController.java
│ │ │ │ ├── user
│ │ │ │ └── UserProfileController.java
│ │ │ │ ├── workspace
│ │ │ │ ├── dsl
│ │ │ │ │ ├── DslEditorResponse.java
│ │ │ │ │ └── DslController.java
│ │ │ │ ├── management
│ │ │ │ │ ├── CreateWorkspaceController.java
│ │ │ │ │ ├── ShareWorkspaceController.java
│ │ │ │ │ ├── WorkspaceSettingsController.java
│ │ │ │ │ ├── UnshareWorkspaceController.java
│ │ │ │ │ ├── PublicWorkspaceController.java
│ │ │ │ │ ├── PrivateWorkspaceController.java
│ │ │ │ │ ├── DeleteBranchController.java
│ │ │ │ │ └── DeleteWorkspaceController.java
│ │ │ │ ├── explore
│ │ │ │ │ ├── ExploreController.java
│ │ │ │ │ ├── ModelController.java
│ │ │ │ │ ├── TreeController.java
│ │ │ │ │ └── GraphController.java
│ │ │ │ ├── diagrams
│ │ │ │ │ └── DiagramEditorController.java
│ │ │ │ ├── AbstractWorkspaceEditorController.java
│ │ │ │ ├── json
│ │ │ │ │ └── JsonController.java
│ │ │ │ └── images
│ │ │ │ │ └── ImagesController.java
│ │ │ │ └── home
│ │ │ │ └── PaginatedWorkspaceList.java
│ │ └── resources
│ │ │ └── log4j2.properties
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── structurizr
│ │ │ └── onpremises
│ │ │ ├── web
│ │ │ ├── api
│ │ │ │ ├── HmacContentTests.java
│ │ │ │ ├── Md5DigestTests.java
│ │ │ │ ├── HashBasedMessageAuthenticationCodeTests.java
│ │ │ │ └── HmacAuthorizationHeaderTests.java
│ │ │ ├── AbstractControllerTests.java
│ │ │ ├── MockSearchComponent.java
│ │ │ ├── ControllerTestsBase.java
│ │ │ ├── MockReviewComponent.java
│ │ │ └── workspace
│ │ │ │ └── management
│ │ │ │ └── CreateWorkspaceControllerTests.java
│ │ │ ├── util
│ │ │ └── HtmlUtilsTests.java
│ │ │ ├── component
│ │ │ └── workspace
│ │ │ │ ├── WorkspaceVersionTests.java
│ │ │ │ ├── WorkspaceBranchTests.java
│ │ │ │ └── MockWorkspaceDao.java
│ │ │ └── configuration
│ │ │ ├── ElasticsearchConfigurerTests.java
│ │ │ └── RedisConfigurerTests.java
│ └── integrationTest
│ │ └── java
│ │ └── com
│ │ └── structurizr
│ │ └── onpremises
│ │ └── component
│ │ └── search
│ │ ├── ApacheLuceneSearchComponentTests.java
│ │ └── ElasticSearchComponentImplTests.java
└── Dockerfile
├── docs
├── README.md
└── docs
│ └── README.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitmodules
├── trivy.sh
├── README.md
├── LICENSE
├── ui.sh
└── .gitignore
/gradle.properties:
--------------------------------------------------------------------------------
1 | structurizrVersion=5.0.2
--------------------------------------------------------------------------------
/structurizr-onpremises-plugin/gradle.properties:
--------------------------------------------------------------------------------
1 | version=1.0.0
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'structurizr-onpremises'
2 | include 'structurizr-onpremises-plugin'
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/fragments/workspace/auto-save.jspf:
--------------------------------------------------------------------------------
1 | <%-- empty file --%>
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/fragments/workspace/auto-refresh.jspf:
--------------------------------------------------------------------------------
1 | <%-- empty file --%>
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | Docs have moved to [https://docs.structurizr.com/onpremises](https://docs.structurizr.com/onpremises).
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/onpremises/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/jboss-web.xml:
--------------------------------------------------------------------------------
1 |
2 | /
3 |
--------------------------------------------------------------------------------
/docs/docs/README.md:
--------------------------------------------------------------------------------
1 | See [https://docs.structurizr.com/onpremises](https://docs.structurizr.com/onpremises) for the on-premises installation documentation.
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/onpremises/HEAD/structurizr-onpremises/src/main/webapp/favicon.ico
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "structurizr-onpremises/src/main/webapp/static/themes"]
2 | path = structurizr-onpremises/src/main/webapp/static/themes
3 | url = https://github.com/structurizr/themes
4 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/AuthenticationMethod.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | public enum AuthenticationMethod {
4 |
5 | LOCAL,
6 | SAML
7 |
8 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/review/ReviewType.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain.review;
2 |
3 | public enum ReviewType {
4 |
5 | General,
6 | Risk,
7 | STRIDE
8 |
9 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/raw-views/json.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="application/json;charset=UTF-8" pageEncoding="UTF-8" %><%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/raw-views/plaintext.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="text/plain;charset=UTF-8" pageEncoding="UTF-8" %><%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/MessageType.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | public enum MessageType {
4 |
5 | success,
6 | info,
7 | warning,
8 | danger
9 |
10 | }
--------------------------------------------------------------------------------
/trivy.sh:
--------------------------------------------------------------------------------
1 | #docker run aquasec/trivy image structurizr/onpremises
2 | docker run -e "TRIVY_DB_REPOSITORY=public.ecr.aws/aquasecurity/trivy-db" -e "TRIVY_JAVA_DB_REPOSITORY=public.ecr.aws/aquasecurity/trivy-java-db" aquasec/trivy image structurizr/onpremises
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/structurizr-onpremises-plugin/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceEventListener.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | public interface WorkspaceEventListener {
4 |
5 | void beforeSave(WorkspaceEvent event);
6 |
7 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/feature-not-available.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Feature not available
4 |
5 | This feature is not avaiable on this installation.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/public-dsl-editor-disabled.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Feature not available
4 |
5 | The DSL editor is not enabled on this installation.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/home.jsp:
--------------------------------------------------------------------------------
1 | <%@ include file="/WEB-INF/fragments/quick-navigation.jspf" %>
2 |
3 |
4 |
5 | <%@ include file="/WEB-INF/fragments/dashboard-workspaces.jspf" %>
6 |
7 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-could-not-be-locked.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Workspace could not be locked
4 |
5 | The workspace could not be locked, please try again.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/StructurizrEnvironmentVariables.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | public class StructurizrEnvironmentVariables {
4 |
5 | public static final String ENCRYPTION_PASSPHRASE = "STRUCTURIZR_ENCRYPTION";
6 |
7 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/dashboard.jsp:
--------------------------------------------------------------------------------
1 | <%@ include file="/WEB-INF/fragments/quick-navigation.jspf" %>
2 |
3 |
4 |
5 | <%@ include file="/WEB-INF/fragments/dashboard-workspaces.jspf" %>
6 |
7 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/RandomGuidGenerator.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | import java.util.UUID;
4 |
5 | public final class RandomGuidGenerator {
6 |
7 | public String generate() {
8 | return UUID.randomUUID().toString();
9 | }
10 |
11 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-is-client-side-encrypted.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Feature not available
4 |
5 | This feature is not available when your workspace is client-side encrypted.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-is-readonly.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Feature not available
4 |
5 | You don't have write access to this workspace, so the editor feature is unavailable.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/structurizr-onpremises-plugin/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceEvent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | public interface WorkspaceEvent {
4 |
5 | WorkspaceProperties getWorkspaceProperties();
6 |
7 | String getJson();
8 |
9 | void setJson(String json);
10 |
11 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/glassfish-web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | /
6 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceMetadataCache.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | interface WorkspaceMetadataCache {
4 |
5 | WorkspaceMetaData get(long workspaceId);
6 |
7 | void put(WorkspaceMetaData workspaceMetaData);
8 |
9 | void stop();
10 |
11 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/DocumentType.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 | class DocumentType {
4 |
5 | static final String WORKSPACE = "workspace";
6 | static final String DIAGRAM = "diagram";
7 | static final String DOCUMENTATION = "documentation";
8 | static final String DECISION = "decision";
9 |
10 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/dsl-editor-disabled.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Feature not available
4 |
5 | The DSL editor is not enabled on this installation
6 | - please use the Structurizr CLI instead.
7 |
8 |
9 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/JsonUtils.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.util.Base64;
5 |
6 | public final class JsonUtils {
7 |
8 | public static String base64(String json) {
9 | return Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8));
10 | }
11 |
12 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/review/ReviewException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.review;
2 |
3 | public class ReviewException extends RuntimeException {
4 |
5 | ReviewException(String message) {
6 | super(message);
7 | }
8 |
9 | ReviewException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/review/ReviewComponentException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.review;
2 |
3 | public class ReviewComponentException extends Exception {
4 |
5 | ReviewComponentException(String message) {
6 | super(message);
7 | }
8 |
9 | ReviewComponentException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/SearchComponentException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 | public class SearchComponentException extends Exception {
4 |
5 | public SearchComponentException(String message) {
6 | super(message);
7 | }
8 |
9 | public SearchComponentException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/review/CommentType.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain.review;
2 |
3 | public enum CommentType {
4 |
5 | General,
6 |
7 | RiskLow,
8 | RiskMedium,
9 | RiskHigh,
10 |
11 | STRIDE_Spoofing,
12 | STRIDE_Tampering,
13 | STRIDE_Repudiation,
14 | STRIDE_InformationDisclosure,
15 | STRIDE_DenialOfService,
16 | STRIDE_ElevationOfPrivilege
17 |
18 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-branches-not-enabled.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Feature not available
4 |
5 | Workspace branches are not enabled on this installation
6 | - see Workspace branches
7 | for details.
8 |
9 |
10 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/ApiException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
7 | public class ApiException extends RuntimeException {
8 |
9 | public ApiException(String message) {
10 | super(message);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/Role.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | import org.springframework.security.core.GrantedAuthority;
4 |
5 | public class Role implements GrantedAuthority {
6 |
7 | private final String name;
8 |
9 | public Role(String name) {
10 | this.name = name;
11 | }
12 |
13 | @Override
14 | public String getAuthority() {
15 | return name;
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/user-profile.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
User profile
4 |
5 |
6 | Username: ${user.username}
7 |
8 |
9 |
10 | Roles:
11 |
12 |
13 |
14 |
15 | - ${role}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceComponentException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | public class WorkspaceComponentException extends RuntimeException {
4 |
5 | public WorkspaceComponentException(String message) {
6 | super(message);
7 | }
8 |
9 | public WorkspaceComponentException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/RawViewResolver.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web;
2 |
3 | import org.springframework.web.servlet.view.UrlBasedViewResolver;
4 |
5 | import java.util.Locale;
6 |
7 | public class RawViewResolver extends UrlBasedViewResolver {
8 |
9 | @Override
10 | protected boolean canHandle(String viewName, Locale locale) {
11 | return "json".equals(viewName) || "plaintext".equals(viewName);
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/HttpHeaders.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | class HttpHeaders {
4 |
5 | public static final String AUTHORIZATION = "Authorization";
6 | public static final String X_AUTHORIZATION = "X-Authorization";
7 | public static final String CONTENT_TYPE = "Content-Type";
8 | public static final String CONTENT_MD5 = "Content-MD5";
9 | public static final String NONCE = "Nonce";
10 |
11 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/EarlyAccessFeaturesNotAvailableException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | public final class EarlyAccessFeaturesNotAvailableException extends RuntimeException {
4 |
5 | public EarlyAccessFeaturesNotAvailableException(String feature) {
6 | super(feature + " is not available in this build - see https://docs.structurizr.com/onpremises for details of how to gain early access to new features");
7 | }
8 |
9 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/UserType.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | public final class UserType {
4 |
5 | public UserType() {
6 | }
7 |
8 | public boolean isAllowedToShareWorkspacesWithLink() {
9 | return true;
10 | }
11 |
12 | public boolean isAllowedToUseImageEmbed() {
13 | return false;
14 | }
15 |
16 | public boolean isAllowedToLockWorkspaces() {
17 | return true;
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Structurizr on-premises installation
2 |
3 | The Structurizr on-premises installation is a standalone version of Structurizr that can be run locally on your own
4 | infrastructure. It’s a Jakarta EE/Spring 6 web application, packaged as a .war file, for deployment into any
5 | compatible Jakarta EE server, such as Apache Tomcat 10. For ease of deployment, by default,
6 | all data is stored on the local file system.
7 |
8 | See [https://docs.structurizr.com/onpremises](https://docs.structurizr.com/onpremises) for documentation.
--------------------------------------------------------------------------------
/structurizr-onpremises/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM tomcat:10.1.41-jre21-temurin-noble
2 | ENV PORT=8080
3 |
4 | RUN set -eux; \
5 | apt-get update; \
6 | apt-get install -y --no-install-recommends graphviz
7 |
8 | RUN sed -i 's/port="8080"/port="${http.port}" maxPostSize="10485760"/' conf/server.xml \
9 | && echo 'export CATALINA_OPTS="-Xms512M -Xmx512M -Dhttp.port=$PORT"' > bin/setenv.sh
10 |
11 | ADD build/libs/structurizr-onpremises.war /usr/local/tomcat/webapps/ROOT.war
12 |
13 | EXPOSE ${PORT}
14 |
15 | CMD ["catalina.sh", "run"]
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/review/component.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/component.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/component.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/review/FileTypeAndContent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.review;
2 |
3 | class FileTypeAndContent {
4 |
5 | private final String extension;
6 | private final byte[] content;
7 |
8 | FileTypeAndContent(String extension, byte[] content) {
9 | this.extension = extension;
10 | this.content = content;
11 | }
12 |
13 | String getExtension() {
14 | return this.extension;
15 | }
16 |
17 | byte[] getContent() {
18 | return content;
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/structurizr-onpremises-plugin/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceProperties.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | import com.structurizr.configuration.User;
4 | import com.structurizr.configuration.Visibility;
5 |
6 | import java.util.Date;
7 | import java.util.Set;
8 |
9 | public interface WorkspaceProperties {
10 |
11 | long getId();
12 |
13 | String getName();
14 |
15 | String getDescription();
16 |
17 | Date getLastModifiedDate();
18 |
19 | Visibility getVisibility();
20 |
21 | Set getUsers();
22 |
23 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/HmacContent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | class HmacContent {
4 |
5 | private final String[] strings;
6 |
7 | public HmacContent(String... strings) {
8 | this.strings = strings;
9 | }
10 |
11 | @Override
12 | public String toString() {
13 | StringBuilder buf = new StringBuilder();
14 | for (String string : strings) {
15 | buf.append(string);
16 | buf.append("\n");
17 | }
18 |
19 | return buf.toString();
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/Configurer.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | import java.util.Properties;
4 |
5 | abstract class Configurer {
6 |
7 | protected Properties properties;
8 |
9 | Configurer(Properties properties) {
10 | this.properties = properties;
11 | }
12 |
13 | void setDefault(String name, String defaultValue) {
14 | if (!properties.containsKey(name)) {
15 | properties.setProperty(name, defaultValue);
16 | }
17 | }
18 |
19 | abstract void apply();
20 |
21 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/NoOpWorkspaceMetadataCache.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | /**
4 | * Workspace metadata cache implementation that does nothing.
5 | */
6 | class NoOpWorkspaceMetadataCache implements WorkspaceMetadataCache {
7 |
8 | @Override
9 | public WorkspaceMetaData get(long workspaceId) {
10 | return null;
11 | }
12 |
13 | @Override
14 | public void put(WorkspaceMetaData workspaceMetaData) {
15 | }
16 |
17 | @Override
18 | public void stop() {
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/HttpUnauthorizedException.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
7 | public class HttpUnauthorizedException extends RuntimeException {
8 |
9 | public HttpUnauthorizedException(String message) {
10 | super(message);
11 | }
12 |
13 | public HttpUnauthorizedException(Throwable cause) {
14 | this(cause.getMessage());
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/WorkspacesApiResponse.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class WorkspacesApiResponse {
7 |
8 | private final List workspaces = new ArrayList<>();
9 |
10 | WorkspacesApiResponse() {
11 | }
12 |
13 | void add(WorkspaceApiResponse war) {
14 | this.workspaces.add(war);
15 | }
16 |
17 | public List getWorkspaces() {
18 | return new ArrayList<>(workspaces);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/api/HmacContentTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 |
7 | public class HmacContentTests {
8 |
9 |
10 | @Test
11 | public void toString_WhenThereAreNoStrings() {
12 | assertEquals("", new HmacContent().toString());
13 | }
14 |
15 | @Test
16 | public void toString_WhenThereAreSomeStrings() {
17 | assertEquals("String1\nString2\nString3\n", new HmacContent("String1", "String2", "String3").toString());
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/views/workspace-locked.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Workspace locked
4 |
5 | This workspace was locked by ${workspace.lockedUser} at .
6 |
7 |
8 |
9 | Workspace summary
10 | |
11 | Diagram viewer
12 |
13 |
14 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/AzureBlobStorageConfigurer.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | import java.util.Properties;
4 |
5 | import static com.structurizr.onpremises.configuration.StructurizrProperties.*;
6 |
7 | public class AzureBlobStorageConfigurer extends Configurer {
8 |
9 | public AzureBlobStorageConfigurer(Properties properties) {
10 | super(properties);
11 | }
12 |
13 | public void apply() {
14 | setDefault(AZURE_BLOB_STORAGE_ACCOUNT_NAME, "");
15 | setDefault(AZURE_BLOB_STORAGE_ACCESS_KEY, "");
16 | setDefault(AZURE_BLOB_STORAGE_CONTAINER_NAME, "");
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/applicationContext-session-local.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/InputStreamAndContentLength.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | import java.io.InputStream;
4 |
5 | public final class InputStreamAndContentLength {
6 |
7 | private final InputStream inputStream;
8 | private final long contentLength;
9 |
10 | public InputStreamAndContentLength(InputStream inputStream, long contentLength) {
11 | this.inputStream = inputStream;
12 | this.contentLength = contentLength;
13 | }
14 |
15 | public InputStream getInputStream() {
16 | return inputStream;
17 | }
18 |
19 | public long getContentLength() {
20 | return contentLength;
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/SearchComponent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 | import com.structurizr.Workspace;
4 |
5 | import java.util.List;
6 | import java.util.Set;
7 |
8 | /**
9 | * Provides search facilities for workspaces.
10 | */
11 | public interface SearchComponent {
12 |
13 | void start();
14 |
15 | void stop();
16 |
17 | boolean isEnabled();
18 |
19 | void index(Workspace workspace) throws SearchComponentException;
20 |
21 | List search(String query, String type, Set workspaceIds) throws SearchComponentException;
22 |
23 | void delete(long workspaceId) throws SearchComponentException;
24 |
25 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/NoOpSpringSessionRepositoryFilter.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web;
2 |
3 | import jakarta.servlet.*;
4 |
5 | import java.io.IOException;
6 |
7 | public class NoOpSpringSessionRepositoryFilter implements Filter {
8 |
9 | @Override
10 | public void init(FilterConfig filterConfig) throws ServletException {
11 | }
12 |
13 | @Override
14 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
15 | filterChain.doFilter(servletRequest, servletResponse);
16 | }
17 |
18 | @Override
19 | public void destroy() {
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/error/Http404Controller.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.error;
2 |
3 | import com.structurizr.onpremises.web.AbstractController;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.ui.ModelMap;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RequestMethod;
8 |
9 | @Controller
10 | public class Http404Controller extends AbstractController {
11 |
12 | @RequestMapping(value = "/404", method = RequestMethod.GET)
13 | public String show404Page(ModelMap model) {
14 | addCommonAttributes(model, "404", true);
15 |
16 | return "404";
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/api/Md5DigestTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 |
7 | public class Md5DigestTests {
8 |
9 | private final Md5Digest md5 = new Md5Digest();
10 |
11 | @Test
12 | public void generate_TreatsNullAsEmptyContent() throws Exception {
13 | assertEquals(md5.generate(null), md5.generate(""));
14 | }
15 |
16 | @Test
17 | public void generate() throws Exception {
18 | assertEquals("ed076287532e86365e841e92bfc50d8c", md5.generate("Hello World!"));
19 | assertEquals("d41d8cd98f00b204e9800998ecf8427e", md5.generate(""));
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/Md5Digest.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import javax.xml.bind.DatatypeConverter;
4 | import java.nio.charset.StandardCharsets;
5 | import java.security.MessageDigest;
6 | import java.security.NoSuchAlgorithmException;
7 |
8 | class Md5Digest {
9 |
10 | private static final String ALGORITHM = "MD5";
11 |
12 | String generate(String content) throws NoSuchAlgorithmException {
13 | if (content == null) {
14 | content = "";
15 | }
16 |
17 | MessageDigest digest = MessageDigest.getInstance(ALGORITHM);
18 | return DatatypeConverter.printHexBinary(digest.digest(content.getBytes(StandardCharsets.UTF_8))).toLowerCase();
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/api/HashBasedMessageAuthenticationCodeTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 |
7 | public class HashBasedMessageAuthenticationCodeTests {
8 |
9 | @Test
10 | public void generate() throws Exception {
11 | // this example is taken from http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
12 | HashBasedMessageAuthenticationCode code = new HashBasedMessageAuthenticationCode("key");
13 | assertEquals("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", code.generate("The quick brown fox jumps over the lazy dog"));
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/AmazonWebServicesS3Configurer.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | import java.util.Properties;
4 |
5 | import static com.structurizr.onpremises.configuration.StructurizrProperties.*;
6 |
7 | class AmazonWebServicesS3Configurer extends Configurer {
8 |
9 | AmazonWebServicesS3Configurer(Properties properties) {
10 | super(properties);
11 | }
12 |
13 | void apply() {
14 | setDefault(AWS_S3_ACCESS_KEY_ID, "");
15 | setDefault(AWS_S3_SECRET_ACCESS_KEY, "");
16 | setDefault(AWS_S3_REGION, "");
17 | setDefault(AWS_S3_BUCKET_NAME, "");
18 | setDefault(AWS_S3_ENDPOINT, "");
19 | setDefault(AWS_S3_PATH_STYLE_ACCESS, "false");
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/security/CsrfSecurityRequestMatcher.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.security;
2 |
3 | import org.springframework.security.web.util.matcher.RequestMatcher;
4 |
5 | import jakarta.servlet.http.HttpServletRequest;
6 |
7 | public class CsrfSecurityRequestMatcher implements RequestMatcher {
8 |
9 | @Override
10 | public boolean matches(HttpServletRequest request) {
11 | String method = request.getMethod();
12 |
13 | if ("POST".equals(method)) {
14 | String uri = request.getRequestURI();
15 |
16 | if (
17 | uri.startsWith("/login")
18 | ) {
19 | return true;
20 | }
21 | }
22 |
23 | return false;
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/applicationContext-security.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/HtmlUtils.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | public final class HtmlUtils {
4 |
5 | public static String filterHtml(String s) {
6 | if (s == null) {
7 | return null;
8 | }
9 |
10 | s = s.replaceAll("<", "");
11 | s = s.replaceAll(">", "");
12 | s = s.replaceAll(" ", "");
13 | s = s.replaceAll("(?s)", "");
14 | s = s.replaceAll("(?s)<[a-zA-Z]{1,10}.*?>", "");
15 | s = s.replaceAll("(?s)[a-zA-Z]{1,10}>", "");
16 |
17 | return s;
18 | }
19 |
20 | public static String escapeQuoteCharacters(String s) {
21 | if (s == null) {
22 | return null;
23 | }
24 |
25 | s = s.replace("'", "\\'");
26 |
27 | return s;
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/error/Http500Controller.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.error;
2 |
3 | import com.structurizr.onpremises.web.AbstractController;
4 | import org.apache.commons.logging.Log;
5 | import org.apache.commons.logging.LogFactory;
6 | import org.springframework.stereotype.Controller;
7 | import org.springframework.ui.ModelMap;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestMethod;
10 |
11 | @Controller
12 | public class Http500Controller extends AbstractController {
13 |
14 | private static Log log = LogFactory.getLog(Http500Controller.class);
15 |
16 | @RequestMapping(value = "/500", method = RequestMethod.GET)
17 | public String showErrorPage(ModelMap model) {
18 | addCommonAttributes(model, "500", true);
19 |
20 | return "500";
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/applicationContext-security-saml.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceLockResponse.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | public class WorkspaceLockResponse {
4 |
5 | private final boolean success;
6 | private final boolean locked;
7 | private String message;
8 |
9 | public WorkspaceLockResponse(boolean success, boolean locked) {
10 | this.success = success;
11 | this.locked = locked;
12 | }
13 |
14 | public WorkspaceLockResponse(boolean success, boolean locked, String message) {
15 | this.success = success;
16 | this.locked = locked;
17 | this.message = message;
18 | }
19 |
20 | public boolean isSuccess() {
21 | return success;
22 | }
23 |
24 | public boolean isLocked() {
25 | return locked;
26 | }
27 |
28 | public String getMessage() {
29 | return message;
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/PropertyPlaceholderConfigurer.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | import java.io.File;
4 | import java.util.Properties;
5 |
6 | public final class PropertyPlaceholderConfigurer extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer {
7 |
8 | private static final String STRUCTURIZR_DATA_DIRECTORY_PROPERTY_NAME = "structurizr.dataDirectory";
9 |
10 | public PropertyPlaceholderConfigurer() {
11 | Properties properties = Configuration.getInstance().getProperties();
12 | File dataDirectory = Configuration.getInstance().getDataDirectory();
13 | properties.setProperty(STRUCTURIZR_DATA_DIRECTORY_PROPERTY_NAME, dataDirectory.getAbsolutePath());
14 |
15 | setLocalOverride(true);
16 | setProperties(properties);
17 | setIgnoreUnresolvablePlaceholders(false);
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/ApiResponse.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | public class ApiResponse {
4 |
5 | private boolean success = false;
6 | private String message = "";
7 |
8 | public ApiResponse(String message) {
9 | this(true, message);
10 | }
11 |
12 | public ApiResponse(boolean success, String message) {
13 | this.success = success;
14 | this.message = message;
15 | }
16 |
17 | ApiResponse(Exception e) {
18 | this(false, e.getMessage());
19 | }
20 |
21 | public boolean isSuccess() {
22 | return success;
23 | }
24 |
25 | void setSuccess(boolean success) {
26 | this.success = success;
27 | }
28 |
29 | public String getMessage() {
30 | return message;
31 | }
32 |
33 | void setMessage(String message) {
34 | this.message = message;
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/Image.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | import java.util.Date;
4 |
5 | public class Image {
6 |
7 | private final String name;
8 | private final long size;
9 | private final Date lastModifiedDate;
10 | private String url;
11 |
12 | public Image(String name, long size, Date lastModifiedDate) {
13 | this.name = name;
14 | this.size = size;
15 | this.lastModifiedDate = lastModifiedDate;
16 | }
17 |
18 | public String getName() {
19 | return name;
20 | }
21 |
22 | public long getSizeInKB() {
23 | return size / 1024;
24 | }
25 |
26 | public Date getLastModifiedDate() {
27 | return lastModifiedDate;
28 | }
29 |
30 | public String getUrl() {
31 | return url;
32 | }
33 |
34 | public void setUrl(String url) {
35 | this.url = url;
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/user/UserProfileController.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.user;
2 |
3 | import com.structurizr.onpremises.web.AbstractController;
4 | import org.springframework.security.access.prepost.PreAuthorize;
5 | import org.springframework.stereotype.Controller;
6 | import org.springframework.ui.ModelMap;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestMethod;
9 |
10 | @Controller
11 | public class UserProfileController extends AbstractController {
12 |
13 | private static final String VIEW = "user-profile";
14 |
15 | @RequestMapping(value = "/user/profile", method = RequestMethod.GET)
16 | @PreAuthorize("isAuthenticated()")
17 | public String showUserProfilePage(ModelMap model) {
18 | model.addAttribute("user", getUser());
19 |
20 | addCommonAttributes(model, "User Profile", true);
21 |
22 | return VIEW;
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/AbstractControllerTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web;
2 |
3 | import com.structurizr.onpremises.web.home.HomePageController;
4 |
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import static org.junit.jupiter.api.Assertions.assertFalse;
9 | import static org.junit.jupiter.api.Assertions.assertTrue;
10 |
11 | public class AbstractControllerTests extends ControllerTestsBase {
12 |
13 | private AbstractController controller;
14 |
15 | @BeforeEach
16 | public void setUp() {
17 | controller = new HomePageController(); // any controller will do
18 | }
19 |
20 | @Test
21 | public void isAuthenticated() {
22 | clearUser();
23 | assertFalse(controller.isAuthenticated());
24 |
25 | setUser("username");
26 | assertTrue(controller.isAuthenticated());
27 |
28 | clearUser();
29 | assertFalse(controller.isAuthenticated());
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/NoOpSearchComponentImpl.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 | import com.structurizr.Workspace;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.Set;
8 |
9 | /**
10 | * A search component implementation that does nothing.
11 | */
12 | class NoOpSearchComponentImpl implements SearchComponent {
13 |
14 | NoOpSearchComponentImpl() {
15 | }
16 |
17 | @Override
18 | public void start() {
19 | }
20 |
21 | @Override
22 | public void stop() {
23 | }
24 |
25 | @Override
26 | public boolean isEnabled() {
27 | return false;
28 | }
29 |
30 | @Override
31 | public void index(Workspace workspace) {
32 | }
33 |
34 | @Override
35 | public List search(String query, String type, Set workspaceIds) {
36 | return new ArrayList<>();
37 | }
38 |
39 | @Override
40 | public void delete(long workspaceId) {
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/MockSearchComponent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web;
2 |
3 | import com.structurizr.Workspace;
4 | import com.structurizr.onpremises.component.search.SearchComponent;
5 | import com.structurizr.onpremises.component.search.SearchComponentException;
6 | import com.structurizr.onpremises.component.search.SearchResult;
7 |
8 | import java.util.List;
9 | import java.util.Set;
10 |
11 | public class MockSearchComponent implements SearchComponent {
12 |
13 | @Override
14 | public void start() {
15 | }
16 |
17 | @Override
18 | public void stop() {
19 | }
20 |
21 | @Override
22 | public boolean isEnabled() {
23 | return true;
24 | }
25 |
26 | @Override
27 | public void index(Workspace workspace) {
28 | }
29 |
30 | @Override
31 | public List search(String query, String type, Set workspaceIds) {
32 | return null;
33 | }
34 |
35 | @Override
36 | public void delete(long workspaceId) {
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/HashBasedMessageAuthenticationCode.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import javax.crypto.Mac;
4 | import javax.crypto.spec.SecretKeySpec;
5 | import javax.xml.bind.DatatypeConverter;
6 | import java.security.InvalidKeyException;
7 | import java.security.NoSuchAlgorithmException;
8 |
9 | class HashBasedMessageAuthenticationCode {
10 |
11 | private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
12 |
13 | private String apiSecret;
14 |
15 | HashBasedMessageAuthenticationCode(String apiSecret) {
16 | this.apiSecret = apiSecret;
17 | }
18 |
19 | String generate(String content) throws NoSuchAlgorithmException, InvalidKeyException {
20 | SecretKeySpec signingKey = new SecretKeySpec(apiSecret.getBytes(), HMAC_SHA256_ALGORITHM);
21 | Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
22 | mac.init(signingKey);
23 | byte[] rawHmac = mac.doFinal(content.getBytes());
24 | return DatatypeConverter.printHexBinary(rawHmac).toLowerCase();
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/Messages.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | import java.io.Serializable;
4 | import java.util.LinkedList;
5 | import java.util.List;
6 | import java.util.Stack;
7 |
8 | public final class Messages implements Serializable {
9 |
10 | private Stack messages = new Stack<>();
11 |
12 | public void addSuccessMessage(String message) {
13 | this.messages.push(new Message(MessageType.success, message));
14 | }
15 |
16 | public void addWarningMessage(String message) {
17 | this.messages.push(new Message(MessageType.warning, message));
18 | }
19 |
20 | public void addErrorMessage(String message) {
21 | this.messages.push(new Message(MessageType.danger, message));
22 | }
23 |
24 | public List getUnreadMessages() {
25 | List unreadMessages = new LinkedList<>();
26 | while (!this.messages.isEmpty()) {
27 | unreadMessages.add(this.messages.pop());
28 | }
29 |
30 | return unreadMessages;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/review/Diagram.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain.review;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class Diagram {
7 |
8 | private int id;
9 | private String url;
10 |
11 | private List comments = new ArrayList<>();
12 |
13 | Diagram() {
14 | }
15 |
16 | public Diagram(int id, String url) {
17 | this.id = id;
18 | this.url = url;
19 | }
20 |
21 | public int getId() {
22 | return id;
23 | }
24 |
25 | void setId(int id) {
26 | this.id = id;
27 | }
28 |
29 | public String getUrl() {
30 | return url;
31 | }
32 |
33 | void setUrl(String url) {
34 | this.url = url;
35 | }
36 |
37 | public List getComments() {
38 | return comments;
39 | }
40 |
41 | public void setComments(List comments) {
42 | this.comments = comments;
43 | }
44 |
45 | void addComment(Comment comment) {
46 | this.comments.add(comment);
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/applicationContext-session-redis.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/security/BcryptController.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.security;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
5 | import org.springframework.web.bind.annotation.PathVariable;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RequestMethod;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @RestController
11 | public class BcryptController {
12 |
13 | private final BCryptPasswordEncoder bCryptPasswordEncoder;
14 |
15 | @Autowired
16 | public BcryptController(BCryptPasswordEncoder bCryptPasswordEncoder) {
17 | this.bCryptPasswordEncoder = bCryptPasswordEncoder;
18 | }
19 |
20 | @RequestMapping(value = "/bcrypt/{plaintext}", method = RequestMethod.GET, produces = "text/plain")
21 | public String getWorkspace(@PathVariable("plaintext") String plaintext) {
22 | return bCryptPasswordEncoder.encode(plaintext) + "\n";
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Structurizr Limited
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/integrationTest/java/com/structurizr/onpremises/component/search/ApacheLuceneSearchComponentTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 |
4 |
5 | import org.junit.jupiter.api.AfterEach;
6 | import org.junit.jupiter.api.BeforeEach;
7 | import org.springframework.util.FileSystemUtils;
8 |
9 | import java.io.File;
10 |
11 | public class ApacheLuceneSearchComponentTests extends AbstractSearchComponentTests {
12 |
13 | private static final File DATA_DIRECTORY = new File("./build/ApacheLuceneSearchComponentTests");
14 |
15 | private ApacheLuceneSearchComponentImpl searchComponent;
16 |
17 | @BeforeEach
18 | public void setUp() {
19 | DATA_DIRECTORY.mkdirs();
20 | searchComponent = new ApacheLuceneSearchComponentImpl(DATA_DIRECTORY);
21 | searchComponent.start();
22 | }
23 |
24 | @AfterEach
25 | public void tearDown() {
26 | searchComponent.stop();
27 | FileSystemUtils.deleteRecursively(DATA_DIRECTORY);
28 | }
29 |
30 | @Override
31 | protected SearchComponent getSearchComponent() {
32 | return searchComponent;
33 | }
34 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/domain/Message.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.domain;
2 |
3 | import java.io.Serializable;
4 |
5 | public final class Message implements Serializable {
6 |
7 | private MessageType type;
8 | private String text;
9 |
10 | public Message(MessageType type, String text) {
11 | this.type = type;
12 | this.text = text;
13 | }
14 |
15 | public MessageType getType() {
16 | return type;
17 | }
18 |
19 | public String getText() {
20 | return text;
21 | }
22 |
23 | @Override
24 | public boolean equals(Object o) {
25 | if (this == o) return true;
26 | if (o == null || getClass() != o.getClass()) return false;
27 |
28 | Message message = (Message) o;
29 |
30 | if (!text.equals(message.text)) return false;
31 | if (type != message.type) return false;
32 |
33 | return true;
34 | }
35 |
36 | @Override
37 | public int hashCode() {
38 | int result = type.hashCode();
39 | result = 31 * result + text.hashCode();
40 | return result;
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/resources/log4j2.properties:
--------------------------------------------------------------------------------
1 | appender.console.type = Console
2 | appender.console.name = LogToConsole
3 | appender.console.layout.type = PatternLayout
4 | appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
5 |
6 | appender.file.type = File
7 | appender.file.name = LogToFile
8 | appender.file.fileName=${sys:structurizr.dataDirectory}/logs/structurizr.log
9 | appender.file.layout.type=PatternLayout
10 | appender.file.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
11 |
12 | logger.app.name = com.structurizr
13 | logger.app.level = info
14 | logger.app.additivity = false
15 | logger.app.appenderRef.console.ref = LogToConsole
16 | logger.app.appenderRef.file.ref = LogToFile
17 |
18 | logger.springSecurity.name = org.springframework.security
19 | logger.springSecurity.level = warn
20 | logger.springSecurity.additivity = false
21 | logger.springSecurity.appenderRef.console.ref = LogToConsole
22 | logger.springSecurity.appenderRef.file.ref = LogToFile
23 |
24 | rootLogger.level = warn
25 | rootLogger.appenderRef.stdout.ref = LogToConsole
26 | rootLogger.appenderRef.file.ref = LogToFile
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/SearchResponse.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 |
5 | import java.util.List;
6 |
7 | class SearchResponse {
8 |
9 | private SearchHits hits;
10 |
11 | SearchResponse() {
12 | }
13 |
14 | public SearchHits getHits() {
15 | return hits;
16 | }
17 |
18 | void setHits(SearchHits hits) {
19 | this.hits = hits;
20 | }
21 |
22 | }
23 |
24 | class SearchHits {
25 |
26 | private List hits;
27 |
28 | public SearchHits() {
29 | }
30 |
31 | public List getHits() {
32 | return hits;
33 | }
34 |
35 | void setHits(List hits) {
36 | this.hits = hits;
37 | }
38 |
39 | }
40 |
41 | class SearchHit {
42 |
43 | @JsonProperty("_source")
44 | private Document source;
45 |
46 | SearchHit() {
47 | }
48 |
49 | public Document getSource() {
50 | return source;
51 | }
52 |
53 | public void setSource(Document source) {
54 | this.source = source;
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/review/ReviewComponent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.review;
2 |
3 | import com.structurizr.onpremises.domain.User;
4 | import com.structurizr.onpremises.domain.review.Review;
5 | import com.structurizr.onpremises.domain.review.ReviewType;
6 | import com.structurizr.onpremises.domain.review.Session;
7 | import com.structurizr.onpremises.domain.InputStreamAndContentLength;
8 |
9 | import java.util.Collection;
10 |
11 | /**
12 | * Provides access to and manages reviews.
13 | */
14 | public interface ReviewComponent {
15 |
16 | public static final String FILE = "file";
17 | public static final String AMAZON_WEB_SERVICES_S3 = "aws-s3";
18 |
19 | Review createReview(User user, Long workspaceId, String[] files, ReviewType type);
20 |
21 | Collection getReviews();
22 |
23 | Review getReview(String reviewId);
24 |
25 | void submitReview(String reviewId, Session reviewSession);
26 |
27 | InputStreamAndContentLength getDiagram(String reviewId, String filename);
28 |
29 | void lockReview(String reviewId);
30 |
31 | void unlockReview(String reviewId);
32 |
33 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/review/ReviewDao.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.review;
2 |
3 | import com.structurizr.onpremises.domain.review.Review;
4 | import com.structurizr.onpremises.domain.review.Session;
5 | import com.structurizr.onpremises.domain.InputStreamAndContentLength;
6 |
7 | import java.util.Collection;
8 | import java.util.Set;
9 |
10 | interface ReviewDao {
11 |
12 | Set getReviewIds() throws ReviewComponentException;
13 |
14 | void putReview(Review review) throws ReviewComponentException;
15 |
16 | Review getReview(String reviewId) throws ReviewComponentException;
17 |
18 | void submitReview(String reviewId, Session reviewSession) throws ReviewComponentException;
19 |
20 | Collection getReviewSessions(String reviewId) throws ReviewComponentException;
21 |
22 | void putDiagram(String reviewId, String filename, byte[] bytes) throws ReviewComponentException;
23 |
24 | boolean reviewExists(String reviewId) throws ReviewComponentException;
25 |
26 | InputStreamAndContentLength getDiagram(String reviewId, String filename) throws ReviewComponentException;
27 |
28 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/util/HtmlUtilsTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 | import static org.junit.jupiter.api.Assertions.assertEquals;
7 |
8 | public class HtmlUtilsTests {
9 |
10 | @Test
11 | public void test_filterHTML() {
12 | assertEquals("Here is some text.", HtmlUtils.filterHtml("Here is some text."));
13 | assertEquals("Here is some text.", HtmlUtils.filterHtml("Here is some text."));
14 | assertEquals("Here is some text.", HtmlUtils.filterHtml("Here is some text."));
15 | assertEquals("Here is a link.", HtmlUtils.filterHtml("Here is a link."));
16 | assertEquals("Here is a link.", HtmlUtils.filterHtml("Here is a link."));
17 | assertEquals("Here is some text", HtmlUtils.filterHtml("Here is <some> text"));
18 | assertEquals("alert('hello')", HtmlUtils.filterHtml(""));
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/api/HmacAuthorizationHeader.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.api;
2 |
3 | import java.util.Base64;
4 |
5 | class HmacAuthorizationHeader {
6 |
7 | private final String apiKey;
8 | private final String hmac;
9 |
10 | HmacAuthorizationHeader(String apiKey, String hmac) {
11 | this.apiKey = apiKey;
12 | this.hmac = hmac;
13 | }
14 |
15 | public String getApiKey() {
16 | return apiKey;
17 | }
18 |
19 | public String getHmac() {
20 | return hmac;
21 | }
22 |
23 | public String format() {
24 | return apiKey + ":" + Base64.getEncoder().encodeToString(hmac.getBytes());
25 | }
26 |
27 | static HmacAuthorizationHeader parse(String s) {
28 | String[] parts = s.split(":");
29 | if (parts.length == 2) {
30 | String apiKey = parts[0];
31 | String hmac = new String(Base64.getDecoder().decode(parts[1]));
32 |
33 | return new HmacAuthorizationHeader(apiKey, hmac);
34 | } else {
35 | throw new IllegalArgumentException("Invalid authorization header");
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/Features.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | public class Features {
4 |
5 | public static final String UI_DSL_EDITOR = "structurizr.feature.ui.dsleditor";
6 | public static final String UI_WORKSPACE_USERS = "structurizr.feature.ui.workspaceusers";
7 | public static final String UI_WORKSPACE_SETTINGS = "structurizr.feature.ui.workspacesettings";
8 |
9 | public static final String WORKSPACE_SEARCH = "structurizr.feature.workspace.search";
10 | public static final String WORKSPACE_ARCHIVING = "structurizr.feature.workspace.archiving";
11 | public static final String WORKSPACE_BRANCHES = "structurizr.feature.workspace.branches";
12 | public static final String WORKSPACE_SCOPE_VALIDATION = "structurizr.feature.workspace.scope";
13 | public static final String WORKSPACE_SCOPE_VALIDATION_STRICT = "strict";
14 | public static final String WORKSPACE_SCOPE_VALIDATION_RELAXED = "relaxed";
15 |
16 | public static final String DIAGRAM_REVIEWS = "structurizr.feature.diagramreviews";
17 |
18 | public static final String DIAGRAM_ANONYMOUS_THUMBNAILS = "structurizr.feature.diagram.anonymousthumbnails";
19 |
20 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/workspace/dsl/DslEditorResponse.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.workspace.dsl;
2 |
3 | public final class DslEditorResponse {
4 |
5 | private final boolean success;
6 | private final String message;
7 | private final int lineNumber;
8 | private final String workspace;
9 |
10 | DslEditorResponse(boolean success, String message) {
11 | this(success, message, 0);
12 | }
13 |
14 | DslEditorResponse(boolean success, String message, int lineNumber) {
15 | this.success = success;
16 | this.message = message;
17 | this.lineNumber = lineNumber;
18 | this.workspace = null;
19 | }
20 |
21 | DslEditorResponse(String workspace) {
22 | this.success = true;
23 | this.workspace = workspace;
24 | this.message = null;
25 | this.lineNumber = 1;
26 | }
27 |
28 | public boolean isSuccess() {
29 | return success;
30 | }
31 |
32 | public String getMessage() {
33 | return message;
34 | }
35 |
36 | public int getLineNumber() {
37 | return lineNumber;
38 | }
39 |
40 | public String getWorkspace() {
41 | return workspace;
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/SamlConfigurer.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | import java.util.Properties;
4 |
5 | class SamlConfigurer extends Configurer {
6 |
7 | SamlConfigurer(Properties properties) {
8 | super(properties);
9 | }
10 |
11 | private static final String DEFAULT_REGISTRATION_ID = "structurizr";
12 |
13 | private static final String DEFAULT_SAML_ATTRIBUTE_USERNAME = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
14 | private static final String DEFAULT_SAML_ATTRIBUTE_ROLE = "http://schemas.xmlsoap.org/claims/Group";
15 |
16 | void apply() {
17 | setDefault(StructurizrProperties.SAML_REGISTRATION_ID, DEFAULT_REGISTRATION_ID);
18 | setDefault(StructurizrProperties.SAML_ENTITY_ID, "");
19 | setDefault(StructurizrProperties.SAML_METADATA, "");
20 | setDefault(StructurizrProperties.SAML_SIGNING_CERTIFICATE, "");
21 | setDefault(StructurizrProperties.SAML_SIGNING_PRIVATE_KEY, "");
22 | setDefault(StructurizrProperties.SAML_ATTRIBUTE_USERNAME, DEFAULT_SAML_ATTRIBUTE_USERNAME);
23 | setDefault(StructurizrProperties.SAML_ATTRIBUTE_ROLE, DEFAULT_SAML_ATTRIBUTE_ROLE);
24 | }
25 |
26 |
27 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/ControllerTestsBase.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web;
2 |
3 | import com.structurizr.onpremises.domain.Role;
4 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
5 | import org.springframework.security.core.Authentication;
6 | import org.springframework.security.core.context.SecurityContextHolder;
7 | import org.springframework.security.core.userdetails.UserDetails;
8 |
9 | import java.util.HashSet;
10 | import java.util.Set;
11 |
12 | public class ControllerTestsBase {
13 |
14 | protected void clearUser() {
15 | SecurityContextHolder.getContext().setAuthentication(null);
16 | }
17 |
18 | protected void setUser(String username, String... roleNames) {
19 | Set roles = new HashSet<>();
20 | for (String roleName : roleNames) {
21 | roles.add(new Role(roleName));
22 | }
23 |
24 | UserDetails userDetails = new org.springframework.security.core.userdetails.User(username, "password", roles);
25 | Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, roles);
26 | SecurityContextHolder.getContext().setAuthentication(authentication);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceBranch.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | import com.structurizr.util.StringUtils;
4 |
5 | public final class WorkspaceBranch {
6 |
7 | public static final String NO_BRANCH = "";
8 | public static final String MAIN_BRANCH = "main";
9 |
10 | private static final String BRANCH_NAME_REGEX = "[a-zA-Z0-9][a-zA-Z0-9-_.]*";
11 | private static final int MAX_LENGTH = 100;
12 |
13 | private final String name;
14 |
15 | public WorkspaceBranch(String name) {
16 | this.name = name;
17 | }
18 |
19 | public static void validateBranchName(String name) {
20 | if (!StringUtils.isNullOrEmpty(name) && !isValidBranchName(name)) {
21 | throw new IllegalArgumentException("The branch name \"" + name + "\" is invalid");
22 | }
23 | }
24 |
25 | public static boolean isMainBranch(String branch) {
26 | return StringUtils.isNullOrEmpty(branch) || branch.equalsIgnoreCase(MAIN_BRANCH);
27 | }
28 |
29 | public String getName() {
30 | return name;
31 | }
32 |
33 | public static boolean isValidBranchName(String name) {
34 | return name.matches(BRANCH_NAME_REGEX) && name.length() < MAX_LENGTH;
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/WorkspaceValidationUtils.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | import com.structurizr.Workspace;
4 | import com.structurizr.onpremises.configuration.Configuration;
5 | import com.structurizr.onpremises.configuration.Features;
6 | import com.structurizr.validation.WorkspaceScopeValidationException;
7 | import com.structurizr.validation.WorkspaceScopeValidatorFactory;
8 |
9 | public class WorkspaceValidationUtils {
10 |
11 | public static void validateWorkspaceScope(Workspace workspace) throws WorkspaceScopeValidationException {
12 | // if workspace scope validation is enabled, reject workspaces without a defined scope
13 | if (Configuration.getInstance().isFeatureEnabled(Features.WORKSPACE_SCOPE_VALIDATION)) {
14 | if (workspace.getConfiguration().getScope() == null) {
15 | throw new WorkspaceScopeValidationException("Strict workspace scope validation has been enabled for this on-premises installation. Unscoped workspaces are not permitted - see https://docs.structurizr.com/workspaces for more information.");
16 | }
17 | }
18 |
19 | // validate workspace scope
20 | WorkspaceScopeValidatorFactory.getValidator(workspace).validate(workspace);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/configuration/StructurizrDataDirectory.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.configuration;
2 |
3 | import com.structurizr.util.StringUtils;
4 |
5 | public final class StructurizrDataDirectory {
6 |
7 | public static final String DEFAULT_DATA_DIRECTORY_PATH = "/usr/local/structurizr";
8 |
9 | private static final String DATA_DIRECTORY_SYSTEM_PROPERTY = "structurizr.dataDirectory";
10 | private static final String DATA_DIRECTORY_ENVIRONMENT_VARIABLE = "STRUCTURIZR_DATA_DIRECTORY";
11 |
12 | public static String getLocation() {
13 | String value = getSystemPropertyIgnoreCase();
14 | if (!StringUtils.isNullOrEmpty(value)) {
15 | return value;
16 | }
17 |
18 | value = System.getenv(DATA_DIRECTORY_ENVIRONMENT_VARIABLE);
19 | if (!StringUtils.isNullOrEmpty(value)) {
20 | return value;
21 | }
22 |
23 | return DEFAULT_DATA_DIRECTORY_PATH;
24 | }
25 |
26 | private static String getSystemPropertyIgnoreCase() {
27 | for (String key : System.getProperties().stringPropertyNames()) {
28 | if (key.equalsIgnoreCase(DATA_DIRECTORY_SYSTEM_PROPERTY)) {
29 | return System.getProperty(key);
30 | }
31 | }
32 |
33 | return null;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/AmazonS3ClientUtils.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | import com.amazonaws.auth.AWSStaticCredentialsProvider;
4 | import com.amazonaws.auth.BasicAWSCredentials;
5 | import com.amazonaws.services.s3.AmazonS3;
6 | import com.amazonaws.services.s3.AmazonS3ClientBuilder;
7 | import com.structurizr.util.StringUtils;
8 |
9 | public class AmazonS3ClientUtils {
10 |
11 | public static AmazonS3 create(String accessKeyId, String secretAccessKey, String region, String endpoint, boolean pathStyleAccessEnabled) {
12 | AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard();
13 |
14 | if (!StringUtils.isNullOrEmpty(endpoint)) {
15 | builder.withEndpointConfiguration(new AmazonS3ClientBuilder.EndpointConfiguration(endpoint, region));
16 | } else if (!StringUtils.isNullOrEmpty(region)) {
17 | builder.withRegion(region);
18 | }
19 |
20 | builder.withPathStyleAccessEnabled(pathStyleAccessEnabled);
21 |
22 | if (!StringUtils.isNullOrEmpty(accessKeyId) && !StringUtils.isNullOrEmpty(secretAccessKey)) {
23 | builder.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKeyId, secretAccessKey)));
24 | }
25 |
26 | return builder.build();
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/web/security/AuthenticationFailureHandler.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web.security;
2 |
3 | import com.structurizr.util.StringUtils;
4 | import org.apache.commons.logging.Log;
5 | import org.apache.commons.logging.LogFactory;
6 | import org.springframework.security.authentication.BadCredentialsException;
7 | import org.springframework.security.core.AuthenticationException;
8 |
9 | import jakarta.servlet.ServletException;
10 | import jakarta.servlet.http.HttpServletRequest;
11 | import jakarta.servlet.http.HttpServletResponse;
12 | import java.io.IOException;
13 |
14 | public class AuthenticationFailureHandler implements org.springframework.security.web.authentication.AuthenticationFailureHandler {
15 |
16 | private static Log log = LogFactory.getLog(AuthenticationFailureHandler.class);
17 |
18 | @Override
19 | public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
20 | String username = httpServletRequest.getParameter("username");
21 |
22 | if (!StringUtils.isNullOrEmpty(username)) {
23 | log.warn(username + " failed authentication: " + e);
24 | } else {
25 | log.warn(e);
26 | }
27 |
28 | httpServletResponse.sendRedirect("/signin?error=true");
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/web/MockReviewComponent.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.web;
2 |
3 | import com.structurizr.onpremises.component.review.ReviewComponent;
4 | import com.structurizr.onpremises.domain.InputStreamAndContentLength;
5 | import com.structurizr.onpremises.domain.User;
6 | import com.structurizr.onpremises.domain.review.Review;
7 | import com.structurizr.onpremises.domain.review.ReviewType;
8 | import com.structurizr.onpremises.domain.review.Session;
9 |
10 | import java.util.Collection;
11 |
12 | public class MockReviewComponent implements ReviewComponent {
13 |
14 | @Override
15 | public Review createReview(User user, Long workspaceId, String[] files, ReviewType type) {
16 | return null;
17 | }
18 |
19 | @Override
20 | public Collection getReviews() {
21 | return null;
22 | }
23 |
24 | @Override
25 | public Review getReview(String reviewId) {
26 | return null;
27 | }
28 |
29 | @Override
30 | public void submitReview(String reviewId, Session reviewSession) {
31 | }
32 |
33 | @Override
34 | public InputStreamAndContentLength getDiagram(String reviewId, String filename) {
35 | return null;
36 | }
37 |
38 | @Override
39 | public void lockReview(String reviewId) {
40 | }
41 |
42 | @Override
43 | public void unlockReview(String reviewId) {
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceVersion.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | import com.structurizr.util.StringUtils;
4 |
5 | import java.util.Date;
6 |
7 | public final class WorkspaceVersion {
8 |
9 | public static final String LATEST_VERSION = "";
10 |
11 | private static final String VERSION_IDENTIFIER_REGEX = "[0-9a-zA-Z+-:_.]*";
12 |
13 | private String versionId;
14 | private final Date lastModifiedDate;
15 |
16 | public WorkspaceVersion(String versionId, Date lastModifiedDate) {
17 | this.versionId = versionId;
18 | this.lastModifiedDate = lastModifiedDate;
19 | }
20 |
21 | public String getVersionId() {
22 | return versionId;
23 | }
24 |
25 | public void clearVersionId() {
26 | this.versionId = null;
27 | }
28 |
29 | public Date getLastModifiedDate() {
30 | return lastModifiedDate;
31 | }
32 |
33 | public static void validateVersionIdentifier(String identifier) {
34 | if (!StringUtils.isNullOrEmpty(identifier) && !isValidBVersionIdentifier(identifier)) {
35 | throw new IllegalArgumentException("The version identifier \"" + identifier + "\" is invalid");
36 | }
37 | }
38 |
39 | public static boolean isValidBVersionIdentifier(String identifier) {
40 | return identifier.matches(VERSION_IDENTIFIER_REGEX);
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/workspace/WorkspaceDao.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 | import com.structurizr.onpremises.domain.Image;
4 | import com.structurizr.onpremises.domain.InputStreamAndContentLength;
5 | import com.structurizr.onpremises.domain.User;
6 |
7 | import java.io.File;
8 | import java.util.Collection;
9 | import java.util.List;
10 |
11 | interface WorkspaceDao {
12 |
13 | List getWorkspaceIds();
14 |
15 | WorkspaceMetaData getWorkspaceMetaData(long workspaceId);
16 |
17 | void putWorkspaceMetaData(WorkspaceMetaData workspaceMetaData);
18 |
19 | long createWorkspace(User user);
20 |
21 | boolean deleteBranch(long workspaceId, String branch);
22 |
23 | boolean deleteWorkspace(long workspaceId);
24 |
25 | String getWorkspace(long workspaceId, String branch, String version);
26 |
27 | void putWorkspace(WorkspaceMetaData workspaceMetaData, String json, String branch);
28 |
29 | List getWorkspaceVersions(long workspaceId, String branch, int maxVersions);
30 |
31 | List getWorkspaceBranches(long workspaceId);
32 |
33 | boolean putImage(long workspaceId, String filename, File file);
34 |
35 | List getImages(long workspaceId);
36 |
37 | InputStreamAndContentLength getImage(long workspaceId, String filename);
38 |
39 | boolean deleteImages(long workspaceId);
40 |
41 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/component/search/Document.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.search;
2 |
3 | final class Document {
4 |
5 | private String url;
6 | private String workspace;
7 | private String name;
8 | private String description;
9 | private String type;
10 | private String content;
11 |
12 | Document() {
13 | }
14 |
15 | public String getUrl() {
16 | return url;
17 | }
18 |
19 | public void setUrl(String url) {
20 | this.url = url;
21 | }
22 |
23 | public String getWorkspace() {
24 | return workspace;
25 | }
26 |
27 | public void setWorkspace(String workspace) {
28 | this.workspace = workspace;
29 | }
30 |
31 | public String getName() {
32 | return name;
33 | }
34 |
35 | public void setName(String name) {
36 | this.name = name;
37 | }
38 |
39 | public String getDescription() {
40 | return description;
41 | }
42 |
43 | public void setDescription(String description) {
44 | this.description = description;
45 | }
46 |
47 | public String getType() {
48 | return type;
49 | }
50 |
51 | public void setType(String type) {
52 | this.type = type;
53 | }
54 |
55 | public String getContent() {
56 | return content;
57 | }
58 |
59 | public void setContent(String content) {
60 | this.content = content;
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/webapp/WEB-INF/applicationContext-security-ldap.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/structurizr-onpremises/src/main/java/com/structurizr/onpremises/util/Version.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.util;
2 |
3 | import java.io.InputStream;
4 | import java.text.DateFormat;
5 | import java.text.SimpleDateFormat;
6 | import java.util.Date;
7 | import java.util.Properties;
8 |
9 | public final class Version {
10 |
11 | private static final String BUILD_PROPERTIES_FILENAME = "build.properties";
12 | private static final String BUILD_VERSION_KEY = "build.number";
13 | private static final String BUILD_TIMESTAMP_KEY = "build.timestamp";
14 |
15 | private static String version;
16 | private static Date buildTimestamp;
17 |
18 | static {
19 | try {
20 | Properties buildProperties = new Properties();
21 | InputStream in = Version.class.getClassLoader().getResourceAsStream(BUILD_PROPERTIES_FILENAME);
22 | DateFormat format = new SimpleDateFormat(DateUtils.ISO_DATE_TIME_FORMAT);
23 | if (in != null) {
24 | buildProperties.load(in);
25 | version = buildProperties.getProperty(BUILD_VERSION_KEY);
26 | buildTimestamp = format.parse(buildProperties.getProperty(BUILD_TIMESTAMP_KEY));
27 | in.close();
28 | }
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | }
33 |
34 | public String getBuildNumber() {
35 | return version;
36 | }
37 |
38 | public Date getBuildTimestamp() {
39 | return buildTimestamp;
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/structurizr-onpremises/src/test/java/com/structurizr/onpremises/component/workspace/WorkspaceVersionTests.java:
--------------------------------------------------------------------------------
1 | package com.structurizr.onpremises.component.workspace;
2 |
3 |
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static org.junit.jupiter.api.Assertions.*;
7 |
8 | public class WorkspaceVersionTests {
9 |
10 | @Test
11 | void isValidBVersionIdentifier() {
12 | assertTrue(WorkspaceVersion.isValidBVersionIdentifier("1234567890")); // local
13 | assertTrue(WorkspaceVersion.isValidBVersionIdentifier("3sL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY+MTRCxf3vjVBH40Nr8X8gdRQBpUMLUo")); // aws
14 | assertTrue(WorkspaceVersion.isValidBVersionIdentifier("h_rKc7NTAmEyxFDB0p4CkCyg_y2uHmO.")); // aws
15 | assertTrue(WorkspaceVersion.isValidBVersionIdentifier("2024-09-07T16:34:45.7048862Z")); // azure
16 |
17 | assertFalse(WorkspaceVersion.isValidBVersionIdentifier("
51 |
52 |