-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
SecretTag.java
175 lines (151 loc) · 6.03 KB
/
SecretTag.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package com.denizenscript.denizencore.objects.core;
import com.denizenscript.denizencore.DenizenCore;
import com.denizenscript.denizencore.objects.Fetchable;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.tags.Attribute;
import com.denizenscript.denizencore.tags.ObjectTagProcessor;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.ReflectionRefuse;
import com.denizenscript.denizencore.utilities.YamlConfiguration;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import java.io.File;
@ReflectionRefuse
public class SecretTag implements ObjectTag {
@ReflectionRefuse
public static YamlConfiguration secretsFile;
public static void load() {
File path = new File(DenizenCore.implementation.getDataFolder(), "secrets.secret");
String fileContent = CoreUtilities.journallingLoadFile(path.getPath());
if (fileContent != null) {
if (!fileContent.startsWith("!SECRETS_FILE")) {
Debug.echoError("Secrets file (Denizen/secrets.secret) is not properly formatted. Refusing to load. Please delete and reset it.");
return;
}
fileContent = fileContent.substring("!SECRETS_FILE".length());
secretsFile = YamlConfiguration.load(fileContent);
}
else {
secretsFile = new YamlConfiguration();
CoreUtilities.journallingFileSave(path.getPath(), "!SECRETS_FILE\n# Denizen Secrets File\n# Refer to meta documentation for SecretTag Objects: https://meta.denizenscript.com/Docs/ObjectTypes/SecretTag"
+ "\n\n# my_token: abc123.123abc\n");
}
}
// <--[ObjectType]
// @name SecretTag
// @prefix secret
// @base ElementTag
// @ExampleTagBase secret[my_secret_key]
// @ExampleValues <secret[my_secret_key]>
// @ExampleForReturns
// - webget %VALUE% "post:Message to secret address!"
// @format
// The identity format for secrets is simply the secret key (as defined by the file 'secrets.secret' in the Denizen folder).
//
// @description
// A SecretTag represents a value that should never be exposed to logs or tags.
// For example: authorization tokens, API keys, etc.
//
// A SecretTag is made of a 'key', and a 'value'.
// The key is a simple name, like 'my_bot_token', that is safe to show in logs/etc.
// The value is the actual internal data that must be kept secret, often a generated code.
//
// The keys and values must be defined in the 'secrets.secret' file inside the Denizen folder.
// The contents of that file would look something like:
// !SECRETS_FILE
// my_bot_token: abc123.123abc
// my_api_key: 1a2b3c4d5e6f
//
// The above example defines SecretTag 'my_bot_token' as 'abc123.123abc',
// meaning you could then use '<secret[my_bot_token]>' in the input to a command that parses secrets to have it understand the real value to input should be 'abc123.123abc'
// However if you use the same tag in for example a narrate command, it would just narrate 'secret@my_bot_token', keeping your real value safe.
// Note that the "!SECRETS_FILE" prefix cannot be removed, but comments can be added/removed/altered freely with a "#" prefix.
//
// Commands that accept SecretTag inputs will document that information in the command meta. For example, see <@link command webget>.
//
// There is intentionally no tag that can read the value of a secret.
//
// You can reload the secrets file via "/denizen reload config"
//
// -->
@Fetchable("secret")
public static SecretTag valueOf(String string, TagContext context) {
if (string.startsWith("secret@")) {
string = string.substring("secret@".length());
}
SecretTag secret = new SecretTag(string);
// Make sure it's valid.
if (secret.isValid()) {
return secret;
}
else {
return null;
}
}
public static boolean matches(String string) {
if (CoreUtilities.toLowerCase(string).startsWith("secret@")) {
return true;
}
return valueOf(string, CoreUtilities.noDebugContext) != null;
}
public SecretTag(String key) {
this.key = key;
}
public String key;
private String prefix = "Secret";
public boolean isValid() {
return getValue() != null;
}
@ReflectionRefuse
public String getValue() {
if (secretsFile == null) {
return null;
}
return secretsFile.getString(key);
}
@Override
public String identify() {
return "secret@" + key;
}
@Override
public String identifySimple() {
return identify();
}
@Override
public String toString() {
return identify();
}
@Override
public String getPrefix() {
return prefix;
}
@Override
public ObjectTag setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
@Override
public String debuggable() {
return "<LG>secret@<Y>" + key;
}
@Override
public boolean isUnique() {
return true;
}
public static void registerTags() {
// <--[tag]
// @attribute <SecretTag.key>
// @returns ElementTag
// @description
// Returns the secret key for this secret object.
// -->
tagProcessor.registerStaticTag(ElementTag.class, "key", (attribute, object) -> {
return new ElementTag(object.key, true);
});
}
public static ObjectTagProcessor<SecretTag> tagProcessor = new ObjectTagProcessor<>();
@Override
public ObjectTag getObjectAttribute(Attribute attribute) {
return tagProcessor.getObjectAttribute(this, attribute);
}
}