1515import com .intellij .openapi .ide .CopyPasteManager ;
1616import com .intellij .openapi .project .Project ;
1717import com .intellij .openapi .ui .ComponentWithBrowseButton ;
18- import com .intellij .openapi .ui .DialogWrapper ;
1918import com .intellij .openapi .ui .TextComponentAccessor ;
2019import com .intellij .openapi .ui .TextFieldWithBrowseButton ;
2120import com .intellij .openapi .ui .ValidationInfo ;
2625import com .intellij .ui .EditorTextField ;
2726import com .intellij .ui .EditorTextFieldProvider ;
2827import com .intellij .ui .SoftWrapsEditorCustomization ;
28+ import com .microsoft .azure .toolkit .intellij .common .AzureCommentLabel ;
29+ import com .microsoft .azure .toolkit .intellij .common .AzureDialog ;
2930import com .microsoft .azure .toolkit .intellij .common .TextDocumentListenerAdapter ;
3031import com .microsoft .azure .toolkit .lib .auth .model .AuthConfiguration ;
3132import com .microsoft .azure .toolkit .lib .auth .model .AuthType ;
33+ import com .microsoft .azure .toolkit .lib .common .form .AzureForm ;
34+ import com .microsoft .azure .toolkit .lib .common .form .AzureFormInput ;
35+ import com .microsoft .azure .toolkit .lib .common .form .AzureValidationInfo ;
3236import com .microsoft .azuretools .azurecommons .util .FileUtil ;
3337import com .microsoft .azuretools .utils .JsonUtils ;
3438import org .apache .commons .lang3 .StringUtils ;
3539import org .jetbrains .annotations .NotNull ;
3640import org .jetbrains .annotations .Nullable ;
3741
42+ import javax .annotation .Nonnull ;
3843import javax .swing .JComponent ;
3944import javax .swing .JPanel ;
4045import javax .swing .JPasswordField ;
4752import java .io .File ;
4853import java .util .ArrayList ;
4954import java .util .Arrays ;
55+ import java .util .Collections ;
5056import java .util .HashMap ;
5157import java .util .HashSet ;
5258import java .util .LinkedHashMap ;
5864import java .util .regex .Pattern ;
5965import java .util .stream .Stream ;
6066
61- public class ServicePrincipalLoginDialog extends DialogWrapper {
67+ public class ServicePrincipalLoginDialog extends AzureDialog <AuthConfiguration > implements AzureForm <AuthConfiguration > {
68+ private static final String GUID_REGEX = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" ; //UUID v1-v5
69+ private static final Pattern GUID_PATTERN = Pattern .compile (GUID_REGEX , Pattern .CASE_INSENSITIVE );
6270 private JTextField clientIdTextField ;
6371 private JTextField tenantIdTextField ;
6472 private JPanel rootPanel ;
@@ -69,17 +77,17 @@ public class ServicePrincipalLoginDialog extends DialogWrapper {
6977 private TextFieldWithBrowseButton certFileTextField ;
7078 private AzureCommentLabel comment ;
7179 private final Project project ;
72- private String memo ; // save current json to avoid infinite update loops
7380 private boolean intermediateState = false ;
81+ private AuthConfiguration auth = new AuthConfiguration ();
7482
75- private static final String GUID_REGEX = "^[{]?[0-9a-fA-F]{8}" + "-([0-9a-fA-F]{4}-)" + "{3}[0-9a-fA-F]{12}[}]?$" ;
7683
77- protected ServicePrincipalLoginDialog (@ Nullable Project project ) {
78- super (project , true );
84+ protected ServicePrincipalLoginDialog (@ Nonnull Project project ) {
85+ super (project );
7986 this .project = project ;
80- init ();
87+
8188 $$$setupUI$$$ (); // tell IntelliJ to call createUIComponents() here.
82- this .setTitle ("Sign in Service Principal" );
89+
90+ init ();
8391 super .setOKButtonText ("Sign In" );
8492
8593 pasteFromClipboard ();
@@ -89,30 +97,27 @@ protected ServicePrincipalLoginDialog(@Nullable Project project) {
8997 FileChooserDescriptor pem = FileChooserDescriptorFactory .createSingleFileDescriptor ("pem" );
9098 pem .withFileFilter (file -> StringUtils .equalsIgnoreCase (file .getExtension (), "pem" ));
9199 certFileTextField .addActionListener (new ComponentWithBrowseButton .BrowseFolderActionListener <>("Select Certificate File" , null , certFileTextField , null ,
92- pem , TextComponentAccessor .TEXT_FIELD_WHOLE_TEXT ) {
100+ pem , TextComponentAccessor .TEXT_FIELD_WHOLE_TEXT ) {
93101 @ Nullable
94102 protected VirtualFile getInitialFile () {
95103 return LocalFileSystem .getInstance ().findFileByPath (FileUtil .getDirectoryWithinUserHome ("/" ).toString ());
96104 }
97105 });
98106
99107 Stream .of (clientIdTextField , tenantIdTextField , keyPasswordField , certFileTextField .getTextField ()).map (JTextComponent ::getDocument )
100- .forEach (document -> document .addDocumentListener (new TextDocumentListenerAdapter () {
101- @ Override
102- public void onDocumentChanged () {
103- uiTextComponents2Json ();
104- }
105- }));
108+ .forEach (document -> document .addDocumentListener (new TextDocumentListenerAdapter () {
109+ @ Override
110+ public void onDocumentChanged () {
111+ uiTextComponents2Json ();
112+ }
113+ }));
106114
107- this .certificateRadioButton .addActionListener (( e ) -> uiTextComponents2Json ());
108- this .passwordRadioButton .addActionListener (( e ) -> uiTextComponents2Json ());
115+ this .certificateRadioButton .addActionListener (e -> uiTextComponents2Json ());
116+ this .passwordRadioButton .addActionListener (e -> uiTextComponents2Json ());
109117
110118 this .jsonDataEditor .addDocumentListener (new DocumentListener () {
111119 @ Override
112120 public void documentChanged (@ NotNull DocumentEvent event ) {
113- if (jsonDataEditor .getText ().equals (memo )) {
114- return ;
115- }
116121 json2UIComponents (jsonDataEditor .getText ());
117122 }
118123 });
@@ -128,43 +133,53 @@ public void documentChanged(@NotNull DocumentEvent event) {
128133 @ Override
129134 protected @ NotNull List <ValidationInfo > doValidateAll () {
130135 List <ValidationInfo > res = new ArrayList <>();
131- AuthConfiguration auth = getData ();
132- if (StringUtils .isBlank (auth .getTenant ())) {
136+ AuthConfiguration data = getData ();
137+ if (StringUtils .isBlank (data .getTenant ())) {
133138 res .add (new ValidationInfo ("tenant is required." , tenantIdTextField ));
134139 }
135- if (!isGuid (auth .getTenant ())) {
136- res .add (new ValidationInfo ("tenant should be a guild ." , tenantIdTextField ));
140+ if (!isGuid (data .getTenant ())) {
141+ res .add (new ValidationInfo ("tenant should be a valid guid ." , tenantIdTextField ));
137142 }
138143
139- if (StringUtils .isBlank (auth .getClient ())) {
144+ if (StringUtils .isBlank (data .getClient ())) {
140145 res .add (new ValidationInfo ("clientId(appId) is required." , clientIdTextField ));
141146 }
142- if (!isGuid (auth .getClient ())) {
143- res .add (new ValidationInfo ("clientId(appId) should be a guild ." , clientIdTextField ));
147+ if (!isGuid (data .getClient ())) {
148+ res .add (new ValidationInfo ("clientId(appId) should be a valid guid ." , clientIdTextField ));
144149 }
145150
146151 if (this .passwordRadioButton .isSelected ()) {
147- if (StringUtils .isBlank (auth .getKey ())) {
152+ if (StringUtils .isBlank (data .getKey ())) {
148153 res .add (new ValidationInfo ("Password is required." , keyPasswordField ));
149154 }
150155 } else {
151- if (StringUtils .isBlank (auth .getCertificate ())) {
156+ if (StringUtils .isBlank (data .getCertificate ())) {
152157 res .add (new ValidationInfo ("Please select a cert file." , certFileTextField ));
153- } else if (!new File (auth .getCertificate ()).exists ()) {
158+ } else if (!new File (data .getCertificate ()).exists ()) {
154159 res .add (new ValidationInfo (String .format ("Cannot find cert file(%s)." , certFileTextField .getText ()), certFileTextField ));
155160 }
156161 }
157162 return res ;
158163 }
159164
165+ @ Override
166+ public AzureForm <AuthConfiguration > getForm () {
167+ return this ;
168+ }
169+
170+ @ Override
171+ protected String getDialogTitle () {
172+ return "Sign In - Service Principal" ;
173+ }
174+
160175 private void syncComponentStatusWhenRadioButtonChanges () {
161176 certFileTextField .setEnabled (certificateRadioButton .isSelected ());
162177 keyPasswordField .setEnabled (passwordRadioButton .isSelected ());
163178 }
164179
165180 private void createUIComponents () {
166181 this .jsonDataEditor = this .buildCodeViewer ();
167- this .comment = new AzureCommentLabel ("You can copy the JSON result of 'az sp create ...' and paste it here" );
182+ this .comment = new AzureCommentLabel ("You can copy the JSON output of 'az sp create ...' and paste it here" );
168183 }
169184
170185 private EditorTextField buildCodeViewer () {
@@ -178,22 +193,37 @@ private EditorTextField buildCodeViewer() {
178193 }
179194
180195 public AuthConfiguration getData () {
181- AuthConfiguration auth = new AuthConfiguration ();
196+ AuthConfiguration data = new AuthConfiguration ();
182197
183- auth .setClient (clientIdTextField .getText ());
184- auth .setTenant (tenantIdTextField .getText ());
198+ data .setClient (clientIdTextField .getText ());
199+ data .setTenant (tenantIdTextField .getText ());
185200 if (passwordRadioButton .isSelected ()) {
186- auth .setKey (String .valueOf (keyPasswordField .getPassword ()));
201+ data .setKey (String .valueOf (keyPasswordField .getPassword ()));
187202 } else {
188- auth .setCertificate (this .certFileTextField .getText ());
203+ data .setCertificate (this .certFileTextField .getText ());
189204 }
190- auth .setType (AuthType .SERVICE_PRINCIPAL );
191- return auth ;
205+ data .setType (AuthType .SERVICE_PRINCIPAL );
206+ return data ;
207+ }
208+
209+ @ Override
210+ public void setData (AuthConfiguration data ) {
211+ this .auth = data ;
212+ }
213+
214+ @ Override
215+ public List <AzureFormInput <?>> getInputs () {
216+ return new ArrayList <>();
217+ }
218+
219+ @ Override
220+ public List <AzureValidationInfo > validateData () {
221+ return Collections .emptyList ();
192222 }
193223
194224 private void pasteFromClipboard () {
195225 String textFromClip = findTextInClipboard (str ->
196- StringUtils .contains (str , "appId" ) && StringUtils .contains (str , "tenant" ) && StringUtils .contains (str , "password" )
226+ StringUtils .contains (str , "appId" ) && StringUtils .contains (str , "tenant" ) && StringUtils .contains (str , "password" )
197227 );
198228 if (StringUtils .isNotBlank (textFromClip )) {
199229 json2UIComponents (textFromClip );
@@ -216,6 +246,8 @@ public static String findTextInClipboard(Predicate<String> func) {
216246 e .printStackTrace ();
217247 }
218248 }
249+ // only for the first clip board
250+ break ;
219251 }
220252
221253 return null ;
@@ -226,47 +258,49 @@ private void uiTextComponents2Json() {
226258 return ;
227259 }
228260 Map <String , String > map = new LinkedHashMap <>();
229- AuthConfiguration auth = getData ();
261+ AuthConfiguration data = getData ();
230262
231263 if (this .certificateRadioButton .isSelected ()) {
232- map .put ("fileWithCertAndPrivateKey" , auth .getCertificate ());
264+ map .put ("fileWithCertAndPrivateKey" , data .getCertificate ());
233265 } else {
234- String password = StringUtils .isNotBlank (auth .getKey ()) ? "<hidden>" : "<empty>" ;
266+ String password = StringUtils .isNotBlank (data .getKey ()) ? "<hidden>" : "<empty>" ;
235267 map .put ("password" , password );
236268 }
237- map .put ("appId" , auth .getClient ());
238- map .put ("tenant" , auth .getTenant ());
269+ map .put ("appId" , data .getClient ());
270+ map .put ("tenant" , data .getTenant ());
239271 String text = JsonUtils .getGson ().toJson (map );
240- memo = text ;
241- this .jsonDataEditor .setText (text );
242- this .jsonDataEditor .setCaretPosition (0 );
272+ if (!StringUtils .equals (jsonDataEditor .getText (), text )) {
273+ this .jsonDataEditor .setText (text );
274+ this .jsonDataEditor .setCaretPosition (0 );
275+ }
243276 }
244277
245278 private void json2UIComponents (String json ) {
246279 intermediateState = true ;
247280 try {
248281 if (StringUtils .isNotBlank (json )) {
249282 try {
250- Map <String , String > map = new HashMap <>();
251- map = JsonUtils .fromJson (json , map .getClass ());
283+ Map <String , String > map = JsonUtils .fromJson (json , HashMap .class );
252284 if (map != null ) {
253- if (map .containsKey ("appId" )) {
254- this .clientIdTextField .setText (StringUtils .defaultString (map .get ("appId" )));
255- }
256-
257- if (map .containsKey ("tenant" )) {
258- this .tenantIdTextField .setText (StringUtils .defaultString (map .get ("tenant" )));
259- }
260-
261- if (map .containsKey ("password" ) && !isPlaceHolder (map .get ("password" ))) {
262- this .passwordRadioButton .setSelected (true );
263- this .keyPasswordField .setText (StringUtils .defaultString (map .get ("password" )));
264- }
265-
266- if (map .containsKey ("fileWithCertAndPrivateKey" )) {
267- this .certificateRadioButton .setSelected (true );
268- this .certFileTextField .setText (StringUtils .defaultString (map .get ("fileWithCertAndPrivateKey" )));
269- }
285+ ApplicationManager .getApplication ().invokeAndWait (() -> {
286+ if (map .containsKey ("appId" )) {
287+ this .clientIdTextField .setText (StringUtils .defaultString (map .get ("appId" )));
288+ }
289+
290+ if (map .containsKey ("tenant" )) {
291+ this .tenantIdTextField .setText (StringUtils .defaultString (map .get ("tenant" )));
292+ }
293+
294+ if (map .containsKey ("password" ) && !isPlaceHolder (map .get ("password" ))) {
295+ this .passwordRadioButton .setSelected (true );
296+ this .keyPasswordField .setText (StringUtils .defaultString (map .get ("password" )));
297+ }
298+
299+ if (map .containsKey ("fileWithCertAndPrivateKey" )) {
300+ this .certificateRadioButton .setSelected (true );
301+ this .certFileTextField .setText (StringUtils .defaultString (map .get ("fileWithCertAndPrivateKey" )));
302+ }
303+ });
270304 }
271305
272306 } catch (JsonSyntaxException ex ) {
@@ -283,11 +317,10 @@ private static boolean isPlaceHolder(String password) {
283317 }
284318
285319 private static boolean isGuid (String str ) {
286- final Pattern p = Pattern .compile (GUID_REGEX );
287320 if (StringUtils .isBlank (str )) {
288321 return false ;
289322 }
290- return p .matcher (str ).matches ();
323+ return GUID_PATTERN .matcher (str ).matches ();
291324 }
292325
293326 // CHECKSTYLE IGNORE check FOR NEXT 1 LINES
0 commit comments