mirror of
https://github.com/makeplane/plane.git
synced 2026-07-14 06:25:58 +02:00
* dev: new license activation workflow for one and pro users (#739) * feat: added feature flag package for decrypting and refreshing the feature flags value * feat: added feature flagging route for route handlers * feat: added feature flag worker for managing and distributing feature flags * feat: implemented feature flag api implementation inside ff handler * feat: added exponential retry interval inside feature flagging * fix: route pattern of feature flag endpoint from monitor * feat: created endpoint to activate workspace from monitor * feat: made changes to accomodate db and activate endpoint * dev: update multi tenant environment settings * dev: workspace license activation flow * dev: update license activate endpoint * dev: update workspace license activate endpoint * feat: added feature flag user sync logic * feat: added constants and sync logic with private key inside * feat: added port as env variable * feat: added modification for removing license key in healthcheck * feat: example env pushed * fix: update monitor endpoints and license activation function * dev: initiate free workspace only for self hosted instances * dev: self hosting license endpoint updates * dev: remove monitor db * dev: update handlers * dev: self hosted license subscription web * dev: removed computed in self hosted subscription * fix: prime server url and delete old license while creating new license in sqlite * chore: subscription activation alert after successfull activation. * dev: update feature flag endpoints * fix: handled several cases of feature flags * fix: linting errors * dev: update monitor to handle workspace activation * dev: update decryption algorithm * feat: separated activation payload with the sync payload * dev: update feature flags endpoint * dev: change type for workspace sync * feat: added isActive flag for user activation * feat: removed creation of free users in monitor * feat: added method for workspace product * dev: add user email in types * feat: added method for workspace product * fix: uer license feature flag fetch * fix: handler fixed for feature key * chore: self managed license activation * chore: update validation license flow. * chore: license activation success. * dev: resync workspace license on activation * chore: plane one success modal. * feat: added constructs to inject private key build time * feat: updated the docker file with private key build argument * feat: added consumption od DEFAULT_PRIVATE_KEY when building monitor * dev: update private key --------- Co-authored-by: Henit Chobisa <chobisa.henit@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> * fix: prime workflow fix for private key generation * dev: remove license check and update release notes * fix: build args for build ci (#916) * fix: GitHub ci build for monitor (#919) * fix: errors in github ci * fix: added base 64 private key * fix: instance register script * fixed monitor for CGO startup failure (#923) * chore: current plan api and server base urls * fix: monitor startup failure --------- Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> * fix: monitor feature flagging (#926) * feat: added startup flag handler inside monitor (#927) * feat: added logs for debugging (#929) * fix: parsing of private key with base64 (#932) * fix: private key parsing for monitor (#934) * fix: throw error if private key is not parsable * fix: printing private key * fix: added print of private key in handler * feat: optimized build for monitor * feat: removed debug statements * feat: added volume for compose * fix: monitor build issues (#935) * fix: success modal fix for self hosted users. (#928) * feat: Added refresh worker job inside monitor for refreshing licenses (#937) * dev: add instance validation endpoints * feat: added startup refresh handler for getting new licenses * feat: updated title --------- Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> * chore: clear workspace license on restart (#939) * fix: product check endpoint for license creation (#941) * feat: added constraints for db models in monitor (#946) * feat: added constraints for db models in monitor * feat: updated constraints * fix: model constraint for db (#948) * fix: one users need to be active for sync (#950) * fix: one users need to be active for sync * fix: added isolation for files for handlers * fix: workspace offline activation for syncing users * fix: update license over syncing in monitor (#954) --------- Co-authored-by: Henit Chobisa <chobisa.henit@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: pushya22 <130810100+pushya22@users.noreply.github.com>
142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
package feat_flag
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/youmark/pkcs8"
|
|
)
|
|
|
|
// File exports utility functions for decrypting feature flags, requiring the
|
|
// private key to be passed in as a parameter.
|
|
|
|
/* ------------------------ Controller Methods ------------------------------- */
|
|
// Takes in the private key and returns the decrypted feature flags
|
|
func GetDecryptedJson(base64EncodedKey string, encryptedFeatureFlag EncryptedData, out interface{}) error {
|
|
// Parse the private key
|
|
rsaPrivateKey, err := ParsePrivateKey(base64EncodedKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse private key: %v", err)
|
|
}
|
|
|
|
// decrypt with the pem key
|
|
|
|
// Decrypt the feature flag
|
|
decryptedData, err := decryptWithPrivateKey(encryptedFeatureFlag, rsaPrivateKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decrypt feature flag: %v", err)
|
|
}
|
|
|
|
// Return the error from the unmarshal function
|
|
return json.Unmarshal(decryptedData, out)
|
|
}
|
|
|
|
func decodeBase64Key(base64EncodedKey string) ([]byte, error) {
|
|
// Remove any whitespace from the encoded key
|
|
base64EncodedKey = strings.TrimSpace(base64EncodedKey)
|
|
|
|
// Remove any line breaks or spaces within the string
|
|
base64EncodedKey = strings.ReplaceAll(base64EncodedKey, "\n", "")
|
|
base64EncodedKey = strings.ReplaceAll(base64EncodedKey, "\r", "")
|
|
base64EncodedKey = strings.ReplaceAll(base64EncodedKey, " ", "")
|
|
|
|
// Check if the length of the string is a multiple of 4, if not, pad with '='
|
|
if len(base64EncodedKey)%4 != 0 {
|
|
padding := 4 - (len(base64EncodedKey) % 4)
|
|
base64EncodedKey += strings.Repeat("=", padding)
|
|
}
|
|
|
|
// Try standard base64 decoding
|
|
decodedKey, err := base64.StdEncoding.DecodeString(base64EncodedKey)
|
|
if err == nil {
|
|
return decodedKey, nil
|
|
}
|
|
|
|
// If both methods fail, return an error
|
|
return nil, fmt.Errorf("failed to decode base64 key: %v", err)
|
|
}
|
|
|
|
/* ---------------------- Helper Functions ---------------------------- */
|
|
// Parses the private key given to the rsa.PrivateKey type
|
|
func ParsePrivateKey(base64EncodedKey string) (*rsa.PrivateKey, error) {
|
|
decodedKey, err := decodeBase64Key(base64EncodedKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode base64 key: %v", err)
|
|
}
|
|
|
|
block, _ := pem.Decode(decodedKey)
|
|
if block == nil {
|
|
return nil, fmt.Errorf("failed to parse PEM block containing the key")
|
|
}
|
|
|
|
privateKey, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse RSA private key: %v", err)
|
|
}
|
|
|
|
key := privateKey.(*rsa.PrivateKey)
|
|
|
|
return key, nil
|
|
}
|
|
|
|
func decryptWithPrivateKey(data EncryptedData, privateKey *rsa.PrivateKey) ([]byte, error) {
|
|
// Decode the base64 encoded AES key
|
|
aesKey, err := base64.StdEncoding.DecodeString(data.AesKey)
|
|
if err != nil {
|
|
fmt.Printf("Error decoding AES key: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
nonce, err := base64.StdEncoding.DecodeString(data.Nonce)
|
|
if err != nil {
|
|
fmt.Printf("Error decoding nonce: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
ciphertext, err := base64.StdEncoding.DecodeString(data.CipherText)
|
|
if err != nil {
|
|
fmt.Printf("Error decoding ciphertext: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
tag, err := base64.StdEncoding.DecodeString(data.Tag)
|
|
if err != nil {
|
|
fmt.Printf("Error decoding tag: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
decryptedAESKey, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, privateKey, aesKey, nil)
|
|
if err != nil {
|
|
fmt.Printf("Error decrypting AES key: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
blockCipher, err := aes.NewCipher(decryptedAESKey)
|
|
if err != nil {
|
|
fmt.Printf("Error creating AES cipher: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
aesGCM, err := cipher.NewGCMWithNonceSize(blockCipher, 16)
|
|
if err != nil {
|
|
fmt.Printf("Error creating GCM: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
plaintext, err := aesGCM.Open(nil, nonce, append(ciphertext, tag...), nil)
|
|
if err != nil {
|
|
fmt.Printf("Error decrypting ciphertext: %v\n", err)
|
|
return nil, err
|
|
}
|
|
|
|
return plaintext, nil
|
|
}
|