- README.md: Build and deployment guide for beginners - Extend-Function.md: Extended files list and version upgrade guide - Unlock-License.md: License check removal documentation - Dockerfile.local: Local source build support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
15 KiB
Mattermost License Unlock Guide
이 문서는 Mattermost Team Edition에서 Enterprise 기능을 라이선스 없이 사용할 수 있도록 수정한 내용을 상세히 설명합니다.
개요
Mattermost Enterprise 기능은 두 가지 레벨에서 제어됩니다:
- 인터페이스 구현체: Enterprise 기능의 실제 코드 (LDAP, SAML, Cluster 등)
- API 라이선스 체크: REST API에서 라이선스 유무 확인
이 프로젝트는 두 가지를 모두 해결합니다:
- Enterprise 구현체를
enterprise-impl/디렉토리에 오픈소스로 제공 - API의 라이선스 체크 코드를 제거
라이선스 체크 제거 상세
1. LDAP API (channels/api4/ldap.go)
1.1 syncLdap() - LDAP 동기화
원본 코드:
func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.syncLdap", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
return
}
// ... 나머지 코드
}
수정 후:
func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionCreateLdapSyncJob) {
c.SetPermissionError(model.PermissionCreateLdapSyncJob)
return
}
// ... 나머지 코드
}
1.2 testLdap() - LDAP 연결 테스트
원본 코드:
func testLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.testLdap", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func testLdap(c *Context, w http.ResponseWriter, r *http.Request) {
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionTestLdap) {
c.SetPermissionError(model.PermissionTestLdap)
return
}
// ...
}
1.3 getLdapGroups() - LDAP 그룹 조회
원본 코드:
func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.getLdapGroups", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementGroups) {
c.SetPermissionError(model.PermissionSysconsoleReadUserManagementGroups)
return
}
// ...
}
1.4 linkLdapGroup() - LDAP 그룹 연결
원본 코드:
func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireRemoteId()
if c.Err != nil {
return
}
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireRemoteId()
if c.Err != nil {
return
}
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementGroups) {
c.SetPermissionError(model.PermissionSysconsoleWriteUserManagementGroups)
return
}
// ...
}
1.5 unlinkLdapGroup() - LDAP 그룹 연결 해제
원본 코드:
func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireRemoteId()
if c.Err != nil {
return
}
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
c.Err = model.NewAppError("Api4.unlinkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireRemoteId()
if c.Err != nil {
return
}
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementGroups) {
c.SetPermissionError(model.PermissionSysconsoleWriteUserManagementGroups)
return
}
// ...
}
1.6 migrateIdLdap() - LDAP ID 마이그레이션
원본 코드:
func migrateIdLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.migrateIdLdap", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func migrateIdLdap(c *Context, w http.ResponseWriter, r *http.Request) {
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
c.SetPermissionError(model.PermissionManageSystem)
return
}
// ...
}
1.7 createLdapPublicCertificate() - LDAP 인증서 생성
원본 코드:
func createLdapPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.createLdapPublicCertificate", "api.ldap.license.error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func createLdapPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request) {
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteAuthenticationLdap) {
c.SetPermissionError(model.PermissionSysconsoleWriteAuthenticationLdap)
return
}
// ...
}
2. Scheme API (channels/api4/scheme.go)
커스텀 권한 스키마 기능의 라이선스 체크를 제거했습니다.
2.1 createScheme() - 스키마 생성
원본 코드:
func createScheme(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError("Api4.CreateScheme", "api.scheme.create_scheme.license.error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func createScheme(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementPermissions) {
c.SetPermissionError(model.PermissionSysconsoleWriteUserManagementPermissions)
return
}
// ...
}
2.2 patchScheme() - 스키마 수정
원본 코드:
func patchScheme(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError("Api4.PatchScheme", "api.scheme.patch_scheme.license.error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func patchScheme(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
// Community Enterprise: License check removed for open source usage
// ...
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementPermissions) {
c.SetPermissionError(model.PermissionSysconsoleWriteUserManagementPermissions)
return
}
// ...
}
2.3 deleteScheme() - 스키마 삭제
원본 코드:
func deleteScheme(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError("Api4.DeleteScheme", "api.scheme.delete_scheme.license.error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func deleteScheme(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementPermissions) {
c.SetPermissionError(model.PermissionSysconsoleWriteUserManagementPermissions)
return
}
// ...
}
3. Group API (channels/api4/group.go)
팀/채널 그룹 기능의 라이선스 체크를 제거했습니다.
3.1 getGroupsByTeamCommon() - 팀별 그룹 조회
원본 코드:
func getGroupsByTeamCommon(c *Context, r *http.Request) ([]byte, *model.AppError) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
return nil, model.NewAppError("Api4.getGroupsByTeam", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
}
// ...
}
수정 후:
func getGroupsByTeamCommon(c *Context, r *http.Request) ([]byte, *model.AppError) {
// ...
// Community Enterprise: License check removed for open source usage
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionViewTeam) {
return nil, c.App.MakePermissionError(c.AppContext.Session(), []*model.Permission{model.PermissionViewTeam})
}
// ...
}
3.2 getGroupsByChannelCommon() - 채널별 그룹 조회
원본 코드:
func getGroupsByChannelCommon(c *Context, r *http.Request) ([]byte, *model.AppError) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAPGroups {
return nil, model.NewAppError("Api4.getGroupsByChannel", "api.ldap_groups.license_error", nil, "", http.StatusForbidden)
}
// ...
}
수정 후:
func getGroupsByChannelCommon(c *Context, r *http.Request) ([]byte, *model.AppError) {
// ...
// Community Enterprise: License check removed for open source usage
// Permission check follows...
// ...
}
4. User API (channels/api4/user.go)
사용자 인증 마이그레이션의 라이선스 체크를 제거했습니다.
4.1 migrateAuthToLdap() - LDAP 인증 마이그레이션
원본 코드:
func migrateAuthToLdap(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError("Api4.migrateAuthToLdap", "api.user.migrate_auth.license.app_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func migrateAuthToLdap(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
// Community Enterprise: License check removed for open source usage
// Email auth in Mattermost system is represented by ""
// ...
}
4.2 migrateAuthToSaml() - SAML 인증 마이그레이션
원본 코드:
func migrateAuthToSaml(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.SAML {
c.Err = model.NewAppError("Api4.migrateAuthToSaml", "api.user.migrate_auth.license.app_error", nil, "", http.StatusForbidden)
return
}
// ...
}
수정 후:
func migrateAuthToSaml(c *Context, w http.ResponseWriter, r *http.Request) {
// ...
// Community Enterprise: License check removed for open source usage
if from == "email" {
from = ""
}
// ...
}
5. Metrics (channels/app/platform/metrics.go)
Prometheus 메트릭 엔드포인트가 404를 반환하던 문제를 수정했습니다.
추가된 인터페이스:
// MetricsHandlerProvider is an interface for metrics implementations that can provide an HTTP handler
type MetricsHandlerProvider interface {
Handler() http.Handler
}
initMetricsRouter()에 추가된 코드:
// Register /metrics handler if metrics implementation provides a handler
if pm.metricsImpl != nil {
if handlerProvider, ok := pm.metricsImpl.(MetricsHandlerProvider); ok {
pm.router.Handle("/metrics", handlerProvider.Handler())
pm.logger.Info("Metrics endpoint registered at /metrics")
}
}
라이선스 체크 패턴
Mattermost 코드에서 라이선스 체크는 일반적으로 다음 패턴을 따릅니다:
// 패턴 1: 단순 nil 체크
if c.App.Channels().License() == nil {
c.Err = model.NewAppError(...)
return
}
// 패턴 2: nil 체크 + 기능 플래그
if c.App.Channels().License() == nil || !*c.App.Channels().License().Features.LDAP {
c.Err = model.NewAppError(...)
return
}
// 패턴 3: 기능 플래그만 체크
if !*c.App.Channels().License().Features.CustomPermissionsSchemes {
c.Err = model.NewAppError(...)
return
}
제거 방법
- 라이선스 체크 조건문 전체를 삭제
- 주석으로 표시:
// Community Enterprise: License check removed for open source usage - 권한 체크만 남김 (보안 유지)
추가 라이선스 체크 위치
다른 Enterprise 기능에도 라이선스 체크가 있을 수 있습니다. 찾는 방법:
# 모든 라이선스 체크 찾기
grep -r "License()" channels/api4/ | grep -v "_test.go"
# 특정 기능의 라이선스 체크
grep -r "Features.SAML" channels/api4/
grep -r "Features.Cluster" channels/api4/
grep -r "Features.Compliance" channels/api4/
grep -r "Features.DataRetention" channels/api4/
grep -r "Features.MessageExport" channels/api4/
UI 라이선스 체크
웹 클라이언트(React)에도 라이선스 체크가 있습니다:
client/
├── *.js # Minified JavaScript
└── *.js.map # Source maps
JavaScript 파일은 minified 상태라 직접 수정이 어렵습니다.
해결 방법 옵션
- webapp 소스 빌드: mattermost/mattermost-webapp 저장소에서 소스를 수정하고 다시 빌드
- API만 사용: API는 라이선스 체크가 제거되었으므로 CLI나 API 호출로 기능 사용
- 커스텀 플러그인: 웹 UI를 우회하는 플러그인 개발
면책 조항
이 수정은 교육 및 개인 학습 목적으로 제공됩니다. 상용 환경에서는 Mattermost 공식 라이선스를 구매하시기 바랍니다.
Enterprise 기능의 전체 지원과 업데이트를 받으려면 공식 라이선스가 필요합니다.